mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-16 16:32:03 +00:00
Merge pull request #2566 from fgiudici/kata-monitor_improvements
Kata monitor: cache improvements
This commit is contained in:
commit
05995632c3
@ -25,7 +25,7 @@ var logLevel = flag.String("log-level", "info", "Log level of logrus(trace/debug
|
|||||||
var (
|
var (
|
||||||
appName = "kata-monitor"
|
appName = "kata-monitor"
|
||||||
// version is the kata monitor version.
|
// version is the kata monitor version.
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
GitCommit = "unknown-commit"
|
GitCommit = "unknown-commit"
|
||||||
)
|
)
|
||||||
|
@ -15,6 +15,7 @@ require (
|
|||||||
github.com/containerd/typeurl v1.0.2
|
github.com/containerd/typeurl v1.0.2
|
||||||
github.com/containernetworking/plugins v0.9.1
|
github.com/containernetworking/plugins v0.9.1
|
||||||
github.com/cri-o/cri-o v1.0.0-rc2.0.20170928185954-3394b3b2d6af
|
github.com/cri-o/cri-o v1.0.0-rc2.0.20170928185954-3394b3b2d6af
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/go-ini/ini v1.28.2
|
github.com/go-ini/ini v1.28.2
|
||||||
github.com/go-openapi/errors v0.18.0
|
github.com/go-openapi/errors v0.18.0
|
||||||
github.com/go-openapi/runtime v0.18.0
|
github.com/go-openapi/runtime v0.18.0
|
||||||
@ -41,7 +42,6 @@ require (
|
|||||||
github.com/urfave/cli v1.22.2
|
github.com/urfave/cli v1.22.2
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
|
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb
|
|
||||||
go.opentelemetry.io/otel v0.15.0
|
go.opentelemetry.io/otel v0.15.0
|
||||||
go.opentelemetry.io/otel/exporters/trace/jaeger v0.15.0
|
go.opentelemetry.io/otel/exporters/trace/jaeger v0.15.0
|
||||||
go.opentelemetry.io/otel/sdk v0.15.0
|
go.opentelemetry.io/otel/sdk v0.15.0
|
||||||
|
@ -538,8 +538,6 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
|||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
@ -183,8 +183,13 @@ func (s *service) mountPprofHandle(m *http.ServeMux, ociSpec *specs.Spec) {
|
|||||||
m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SocketAddress returns the address of the abstract domain socket for communicating with the
|
// GetSandboxesStoragePath returns the storage path where sandboxes info are stored
|
||||||
|
func GetSanboxesStoragePath() string {
|
||||||
|
return filepath.Join(string(filepath.Separator), "run", "vc", "sbs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketAddress returns the address of the unix domain socket for communicating with the
|
||||||
// shim management endpoint
|
// shim management endpoint
|
||||||
func SocketAddress(id string) string {
|
func SocketAddress(id string) string {
|
||||||
return fmt.Sprintf("unix://%s", filepath.Join(string(filepath.Separator), "run", "vc", "sbs", id, "shim-monitor.sock"))
|
return fmt.Sprintf("unix://%s", filepath.Join(string(filepath.Separator), GetSanboxesStoragePath(), id, "shim-monitor.sock"))
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,12 @@ package katamonitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/xeipuuv/gojsonpointer"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
pb "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
pb "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
|
||||||
@ -117,14 +114,12 @@ func parseEndpoint(endpoint string) (string, string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSandboxes get kata sandbox from the container engine.
|
// getSandboxes gets ready sandboxes from the container engine and returns an updated sandboxMap
|
||||||
// this will be called only after monitor start.
|
func (km *KataMonitor) getSandboxes(sandboxMap map[string]bool) (map[string]bool, error) {
|
||||||
func (km *KataMonitor) getSandboxes() (map[string]struct{}, error) {
|
newMap := make(map[string]bool)
|
||||||
|
|
||||||
sandboxMap := make(map[string]struct{})
|
|
||||||
runtimeClient, runtimeConn, err := getRuntimeClient(km.runtimeEndpoint)
|
runtimeClient, runtimeConn, err := getRuntimeClient(km.runtimeEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sandboxMap, err
|
return newMap, err
|
||||||
}
|
}
|
||||||
defer closeConnection(runtimeConn)
|
defer closeConnection(runtimeConn)
|
||||||
|
|
||||||
@ -140,61 +135,26 @@ func (km *KataMonitor) getSandboxes() (map[string]struct{}, error) {
|
|||||||
monitorLog.Debugf("ListPodSandboxRequest: %v", request)
|
monitorLog.Debugf("ListPodSandboxRequest: %v", request)
|
||||||
r, err := runtimeClient.ListPodSandbox(context.Background(), request)
|
r, err := runtimeClient.ListPodSandbox(context.Background(), request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sandboxMap, err
|
return newMap, err
|
||||||
}
|
}
|
||||||
monitorLog.Debugf("ListPodSandboxResponse: %v", r)
|
monitorLog.Debugf("ListPodSandboxResponse: %v", r)
|
||||||
|
|
||||||
for _, pod := range r.Items {
|
for _, pod := range r.Items {
|
||||||
request := &pb.PodSandboxStatusRequest{
|
// Use the cached data if available
|
||||||
PodSandboxId: pod.Id,
|
if isKata, ok := sandboxMap[pod.Id]; ok {
|
||||||
Verbose: true,
|
newMap[pod.Id] = isKata
|
||||||
}
|
|
||||||
|
|
||||||
r, err := runtimeClient.PodSandboxStatus(context.Background(), request)
|
|
||||||
if err != nil {
|
|
||||||
return sandboxMap, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lowRuntime := ""
|
|
||||||
var res map[string]interface{}
|
|
||||||
if err := json.Unmarshal([]byte(r.Info["info"]), &res); err != nil {
|
|
||||||
monitorLog.WithError(err).WithField("pod", r).Error("failed to Unmarshal pod info")
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
monitorLog.WithField("pod info", res).Debug("")
|
|
||||||
|
|
||||||
// get low level container runtime
|
|
||||||
// containerd stores the pod runtime in "/runtimeType" while CRI-O stores it the
|
|
||||||
// io.kubernetes.cri-o.RuntimeHandler annotation: check for both.
|
|
||||||
keys := []string{"/runtimeType", "/runtimeSpec/annotations/io.kubernetes.cri-o.RuntimeHandler"}
|
|
||||||
for _, key := range keys {
|
|
||||||
pointer, _ := gojsonpointer.NewJsonPointer(key)
|
|
||||||
rt, _, _ := pointer.Get(res)
|
|
||||||
if rt != nil {
|
|
||||||
if str, ok := rt.(string); ok {
|
|
||||||
lowRuntime = str
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If lowRuntime is empty something changed in containerd/CRI-O or we are dealing with an unknown container engine.
|
|
||||||
// Safest options is to add the POD in the list: we will be able to connect to the shim to retrieve the actual info
|
|
||||||
// only for kata PODs.
|
|
||||||
if lowRuntime == "" {
|
|
||||||
monitorLog.WithField("pod", r).Info("unable to retrieve the runtime type")
|
|
||||||
sandboxMap[pod.Id] = struct{}{}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a directory associated with the POD ID exist on the kata fs:
|
||||||
|
// if so we know that the POD is a kata one.
|
||||||
|
newMap[pod.Id] = checkSandboxFSExists(pod.Id)
|
||||||
monitorLog.WithFields(logrus.Fields{
|
monitorLog.WithFields(logrus.Fields{
|
||||||
"low runtime": lowRuntime,
|
"id": pod.Id,
|
||||||
|
"is kata": newMap[pod.Id],
|
||||||
|
"pod": pod,
|
||||||
}).Debug("")
|
}).Debug("")
|
||||||
if strings.Contains(lowRuntime, "kata") {
|
|
||||||
sandboxMap[pod.Id] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sandboxMap, nil
|
return newMap, nil
|
||||||
}
|
}
|
||||||
|
@ -140,8 +140,8 @@ func encodeMetricFamily(mfs []*dto.MetricFamily, encoder expfmt.Encoder) error {
|
|||||||
|
|
||||||
// aggregateSandboxMetrics will get metrics from one sandbox and do some process
|
// aggregateSandboxMetrics will get metrics from one sandbox and do some process
|
||||||
func (km *KataMonitor) aggregateSandboxMetrics(encoder expfmt.Encoder) error {
|
func (km *KataMonitor) aggregateSandboxMetrics(encoder expfmt.Encoder) error {
|
||||||
// get all sandboxes from cache
|
// get all kata sandboxes from cache
|
||||||
sandboxes := km.sandboxCache.getAllSandboxes()
|
sandboxes := km.sandboxCache.getKataSandboxes()
|
||||||
// save running kata pods as a metrics.
|
// save running kata pods as a metrics.
|
||||||
runningShimCount.Set(float64(len(sandboxes)))
|
runningShimCount.Set(float64(len(sandboxes)))
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ func (km *KataMonitor) aggregateSandboxMetrics(encoder expfmt.Encoder) error {
|
|||||||
monitorLog.WithField("sandbox_count", len(sandboxes)).Debugf("sandboxes count")
|
monitorLog.WithField("sandbox_count", len(sandboxes)).Debugf("sandboxes count")
|
||||||
|
|
||||||
// get metrics from sandbox's shim
|
// get metrics from sandbox's shim
|
||||||
for sandboxID := range sandboxes {
|
for _, sandboxID := range sandboxes {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(sandboxID string, results chan<- []*dto.MetricFamily) {
|
go func(sandboxID string, results chan<- []*dto.MetricFamily) {
|
||||||
sandboxMetrics, err := getParsedMetrics(sandboxID)
|
sandboxMetrics, err := getParsedMetrics(sandboxID)
|
||||||
|
@ -9,10 +9,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +23,8 @@ var monitorLog = logrus.WithField("source", "kata-monitor")
|
|||||||
const (
|
const (
|
||||||
RuntimeContainerd = "containerd"
|
RuntimeContainerd = "containerd"
|
||||||
RuntimeCRIO = "cri-o"
|
RuntimeCRIO = "cri-o"
|
||||||
podCacheRefreshTimeSeconds = 15
|
fsMonitorRetryDelaySeconds = 60
|
||||||
|
podCacheRefreshDelaySeconds = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetLogger sets the logger for katamonitor package.
|
// SetLogger sets the logger for katamonitor package.
|
||||||
@ -50,7 +53,7 @@ func NewKataMonitor(runtimeEndpoint string) (*KataMonitor, error) {
|
|||||||
runtimeEndpoint: runtimeEndpoint,
|
runtimeEndpoint: runtimeEndpoint,
|
||||||
sandboxCache: &sandboxCache{
|
sandboxCache: &sandboxCache{
|
||||||
Mutex: &sync.Mutex{},
|
Mutex: &sync.Mutex{},
|
||||||
sandboxes: make(map[string]struct{}),
|
sandboxes: make(map[string]bool),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,16 +67,75 @@ func NewKataMonitor(runtimeEndpoint string) (*KataMonitor, error) {
|
|||||||
|
|
||||||
// startPodCacheUpdater will boot a thread to manage sandbox cache
|
// startPodCacheUpdater will boot a thread to manage sandbox cache
|
||||||
func (km *KataMonitor) startPodCacheUpdater() {
|
func (km *KataMonitor) startPodCacheUpdater() {
|
||||||
|
sbsWatcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
monitorLog.WithError(err).Fatal("failed to setup sandbox events watcher")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer sbsWatcher.Close()
|
||||||
for {
|
for {
|
||||||
time.Sleep(podCacheRefreshTimeSeconds * time.Second)
|
err = sbsWatcher.Add(getSandboxFS())
|
||||||
sandboxes, err := km.getSandboxes()
|
if err != nil {
|
||||||
|
// if there are no kata pods (yet), the kata /run/vc directory may not be there: retry later
|
||||||
|
monitorLog.WithError(err).Warnf("cannot monitor %s, retry in %d sec.", getSandboxFS(), fsMonitorRetryDelaySeconds)
|
||||||
|
time.Sleep(fsMonitorRetryDelaySeconds * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
monitorLog.Debugf("started fs monitoring @%s", getSandboxFS())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// we refresh the pod cache once if we get multiple add/delete pod events in a short time (< podCacheRefreshDelaySeconds)
|
||||||
|
cacheUpdateTimer := time.NewTimer(podCacheRefreshDelaySeconds * time.Second)
|
||||||
|
cacheUpdateTimerWasSet := false
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-sbsWatcher.Events:
|
||||||
|
if !ok {
|
||||||
|
monitorLog.WithError(err).Fatal("cannot watch sandboxes fs")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
monitorLog.WithField("event", event).Debug("got sandbox event")
|
||||||
|
switch event.Op {
|
||||||
|
case fsnotify.Create:
|
||||||
|
splitPath := strings.Split(event.Name, string(os.PathSeparator))
|
||||||
|
id := splitPath[len(splitPath)-1]
|
||||||
|
if !km.sandboxCache.putIfNotExists(id, true) {
|
||||||
|
monitorLog.WithField("pod", id).Warn(
|
||||||
|
"CREATE event but pod already present in the sandbox cache")
|
||||||
|
}
|
||||||
|
monitorLog.WithField("pod", id).Info("sandbox cache: added pod")
|
||||||
|
|
||||||
|
case fsnotify.Remove:
|
||||||
|
splitPath := strings.Split(event.Name, string(os.PathSeparator))
|
||||||
|
id := splitPath[len(splitPath)-1]
|
||||||
|
if !km.sandboxCache.deleteIfExists(id) {
|
||||||
|
monitorLog.WithField("pod", id).Warn(
|
||||||
|
"REMOVE event but pod was missing from the sandbox cache")
|
||||||
|
}
|
||||||
|
monitorLog.WithField("pod", id).Info("sandbox cache: removed pod")
|
||||||
|
|
||||||
|
default:
|
||||||
|
monitorLog.WithField("event", event).Warn("got unexpected fs event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// While we process fs events directly to update the sandbox cache we need to sync with the
|
||||||
|
// container engine to ensure we are on sync with it: we can get out of sync in environments
|
||||||
|
// where kata workloads can be started by other processes than the container engine.
|
||||||
|
cacheUpdateTimerWasSet = cacheUpdateTimer.Reset(podCacheRefreshDelaySeconds * time.Second)
|
||||||
|
monitorLog.WithField("was reset", cacheUpdateTimerWasSet).Debugf(
|
||||||
|
"cache update timer fires in %d secs", podCacheRefreshDelaySeconds)
|
||||||
|
|
||||||
|
case <-cacheUpdateTimer.C:
|
||||||
|
sandboxes, err := km.getSandboxes(km.sandboxCache.getAllSandboxes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
monitorLog.WithError(err).Error("failed to get sandboxes")
|
monitorLog.WithError(err).Error("failed to get sandboxes")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
monitorLog.WithField("count", len(sandboxes)).Debug("update sandboxes list")
|
monitorLog.WithField("count", len(sandboxes)).Info("synced sandbox cache with the container engine")
|
||||||
|
monitorLog.WithField("sandboxes", sandboxes).Debug("dump sandbox cache")
|
||||||
km.sandboxCache.set(sandboxes)
|
km.sandboxCache.set(sandboxes)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAgentURL returns agent URL
|
// GetAgentURL returns agent URL
|
||||||
@ -95,20 +157,8 @@ func (km *KataMonitor) GetAgentURL(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// ListSandboxes list all sandboxes running in Kata
|
// ListSandboxes list all sandboxes running in Kata
|
||||||
func (km *KataMonitor) ListSandboxes(w http.ResponseWriter, r *http.Request) {
|
func (km *KataMonitor) ListSandboxes(w http.ResponseWriter, r *http.Request) {
|
||||||
sandboxes := km.getSandboxList()
|
sandboxes := km.sandboxCache.getKataSandboxes()
|
||||||
for _, s := range sandboxes {
|
for _, s := range sandboxes {
|
||||||
w.Write([]byte(fmt.Sprintf("%s\n", s)))
|
w.Write([]byte(fmt.Sprintf("%s\n", s)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (km *KataMonitor) getSandboxList() []string {
|
|
||||||
sn := km.sandboxCache.getAllSandboxes()
|
|
||||||
result := make([]string, len(sn))
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for k := range sn {
|
|
||||||
result[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
@ -11,15 +11,28 @@ import (
|
|||||||
|
|
||||||
type sandboxCache struct {
|
type sandboxCache struct {
|
||||||
*sync.Mutex
|
*sync.Mutex
|
||||||
sandboxes map[string]struct{}
|
// the bool value tracks if the pod is a kata one (true) or not (false)
|
||||||
|
sandboxes map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *sandboxCache) getAllSandboxes() map[string]struct{} {
|
func (sc *sandboxCache) getAllSandboxes() map[string]bool {
|
||||||
sc.Lock()
|
sc.Lock()
|
||||||
defer sc.Unlock()
|
defer sc.Unlock()
|
||||||
return sc.sandboxes
|
return sc.sandboxes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *sandboxCache) getKataSandboxes() []string {
|
||||||
|
sc.Lock()
|
||||||
|
defer sc.Unlock()
|
||||||
|
var katasandboxes []string
|
||||||
|
for id, isKata := range sc.sandboxes {
|
||||||
|
if isKata {
|
||||||
|
katasandboxes = append(katasandboxes, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return katasandboxes
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *sandboxCache) deleteIfExists(id string) bool {
|
func (sc *sandboxCache) deleteIfExists(id string) bool {
|
||||||
sc.Lock()
|
sc.Lock()
|
||||||
defer sc.Unlock()
|
defer sc.Unlock()
|
||||||
@ -33,12 +46,12 @@ func (sc *sandboxCache) deleteIfExists(id string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *sandboxCache) putIfNotExists(id string) bool {
|
func (sc *sandboxCache) putIfNotExists(id string, value bool) bool {
|
||||||
sc.Lock()
|
sc.Lock()
|
||||||
defer sc.Unlock()
|
defer sc.Unlock()
|
||||||
|
|
||||||
if _, found := sc.sandboxes[id]; !found {
|
if _, found := sc.sandboxes[id]; !found {
|
||||||
sc.sandboxes[id] = struct{}{}
|
sc.sandboxes[id] = value
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +59,7 @@ func (sc *sandboxCache) putIfNotExists(id string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *sandboxCache) set(sandboxes map[string]struct{}) {
|
func (sc *sandboxCache) set(sandboxes map[string]bool) {
|
||||||
sc.Lock()
|
sc.Lock()
|
||||||
defer sc.Unlock()
|
defer sc.Unlock()
|
||||||
sc.sandboxes = sandboxes
|
sc.sandboxes = sandboxes
|
||||||
|
@ -16,10 +16,10 @@ func TestSandboxCache(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
sc := &sandboxCache{
|
sc := &sandboxCache{
|
||||||
Mutex: &sync.Mutex{},
|
Mutex: &sync.Mutex{},
|
||||||
sandboxes: make(map[string]struct{}),
|
sandboxes: make(map[string]bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
scMap := map[string]struct{}{"111": {}}
|
scMap := map[string]bool{"111": true}
|
||||||
|
|
||||||
sc.set(scMap)
|
sc.set(scMap)
|
||||||
|
|
||||||
@ -28,12 +28,12 @@ func TestSandboxCache(t *testing.T) {
|
|||||||
|
|
||||||
// put new item
|
// put new item
|
||||||
id := "new-id"
|
id := "new-id"
|
||||||
b := sc.putIfNotExists(id)
|
b := sc.putIfNotExists(id, true)
|
||||||
assert.Equal(true, b)
|
assert.Equal(true, b)
|
||||||
assert.Equal(2, len(scMap))
|
assert.Equal(2, len(scMap))
|
||||||
|
|
||||||
// put key that alreay exists
|
// put key that alreay exists
|
||||||
b = sc.putIfNotExists(id)
|
b = sc.putIfNotExists(id, true)
|
||||||
assert.Equal(false, b)
|
assert.Equal(false, b)
|
||||||
|
|
||||||
b = sc.deleteIfExists(id)
|
b = sc.deleteIfExists(id)
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cdshim "github.com/containerd/containerd/runtime/v2/shim"
|
cdshim "github.com/containerd/containerd/runtime/v2/shim"
|
||||||
@ -37,6 +39,17 @@ func getSandboxIDFromReq(r *http.Request) (string, error) {
|
|||||||
return "", fmt.Errorf("sandbox not found in %+v", r.URL.Query())
|
return "", fmt.Errorf("sandbox not found in %+v", r.URL.Query())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSandboxFS() string {
|
||||||
|
return shim.GetSanboxesStoragePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSandboxFSExists(sandboxID string) bool {
|
||||||
|
sbsPath := filepath.Join(string(filepath.Separator), getSandboxFS(), sandboxID)
|
||||||
|
_, err := os.Stat(sbsPath)
|
||||||
|
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
// BuildShimClient builds and returns an http client for communicating with the provided sandbox
|
// BuildShimClient builds and returns an http client for communicating with the provided sandbox
|
||||||
func BuildShimClient(sandboxID string, timeout time.Duration) (*http.Client, error) {
|
func BuildShimClient(sandboxID string, timeout time.Duration) (*http.Client, error) {
|
||||||
return buildUnixSocketClient(shim.SocketAddress(sandboxID), timeout)
|
return buildUnixSocketClient(shim.SocketAddress(sandboxID), timeout)
|
||||||
|
12
src/runtime/vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
Normal file
12
src/runtime/vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
1
src/runtime/vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
1
src/runtime/vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
go.sum linguist-generated
|
6
src/runtime/vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
Normal file
6
src/runtime/vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# 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
|
||||||
|
*.sublime-project
|
36
src/runtime/vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
Normal file
36
src/runtime/vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- "stable"
|
||||||
|
- "1.11.x"
|
||||||
|
- "1.10.x"
|
||||||
|
- "1.9.x"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- go: "stable"
|
||||||
|
env: GOLINT=true
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test --race ./...
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
|
||||||
|
- if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi
|
||||||
|
- go vet ./...
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
- windows
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
52
src/runtime/vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
Normal file
52
src/runtime/vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Names should be added to this file as
|
||||||
|
# Name or Organization <email address>
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
Aaron L <aaron@bettercoder.net>
|
||||||
|
Adrien Bustany <adrien@bustany.org>
|
||||||
|
Amit Krishnan <amit.krishnan@oracle.com>
|
||||||
|
Anmol Sethi <me@anmol.io>
|
||||||
|
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
||||||
|
Bruno Bigras <bigras.bruno@gmail.com>
|
||||||
|
Caleb Spare <cespare@gmail.com>
|
||||||
|
Case Nelson <case@teammating.com>
|
||||||
|
Chris Howey <chris@howey.me> <howeyc@gmail.com>
|
||||||
|
Christoffer Buchholz <christoffer.buchholz@gmail.com>
|
||||||
|
Daniel Wagner-Hall <dawagner@gmail.com>
|
||||||
|
Dave Cheney <dave@cheney.net>
|
||||||
|
Evan Phoenix <evan@fallingsnow.net>
|
||||||
|
Francisco Souza <f@souza.cc>
|
||||||
|
Hari haran <hariharan.uno@gmail.com>
|
||||||
|
John C Barstow
|
||||||
|
Kelvin Fo <vmirage@gmail.com>
|
||||||
|
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
|
||||||
|
Matt Layher <mdlayher@gmail.com>
|
||||||
|
Nathan Youngman <git@nathany.com>
|
||||||
|
Nickolai Zeldovich <nickolai@csail.mit.edu>
|
||||||
|
Patrick <patrick@dropbox.com>
|
||||||
|
Paul Hammond <paul@paulhammond.org>
|
||||||
|
Pawel Knap <pawelknap88@gmail.com>
|
||||||
|
Pieter Droogendijk <pieter@binky.org.uk>
|
||||||
|
Pursuit92 <JoshChase@techpursuit.net>
|
||||||
|
Riku Voipio <riku.voipio@linaro.org>
|
||||||
|
Rob Figueiredo <robfig@gmail.com>
|
||||||
|
Rodrigo Chiossi <rodrigochiossi@gmail.com>
|
||||||
|
Slawek Ligus <root@ooz.ie>
|
||||||
|
Soge Zhang <zhssoge@gmail.com>
|
||||||
|
Tiffany Jernigan <tiffany.jernigan@intel.com>
|
||||||
|
Tilak Sharma <tilaks@google.com>
|
||||||
|
Tom Payne <twpayne@gmail.com>
|
||||||
|
Travis Cline <travis.cline@gmail.com>
|
||||||
|
Tudor Golubenco <tudor.g@gmail.com>
|
||||||
|
Vahe Khachikyan <vahe@live.ca>
|
||||||
|
Yukang <moorekang@gmail.com>
|
||||||
|
bronze1man <bronze1man@gmail.com>
|
||||||
|
debrando <denis.brandolini@gmail.com>
|
||||||
|
henrikedwards <henrik.edwards@gmail.com>
|
||||||
|
铁哥 <guotie.9@gmail.com>
|
317
src/runtime/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
Normal file
317
src/runtime/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v1.4.7 / 2018-01-09
|
||||||
|
|
||||||
|
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
|
||||||
|
* Tests: Fix missing verb on format string (thanks @rchiossi)
|
||||||
|
* Linux: Fix deadlock in Remove (thanks @aarondl)
|
||||||
|
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
|
||||||
|
* Docs: Moved FAQ into the README (thanks @vahe)
|
||||||
|
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
|
||||||
|
* Docs: replace references to OS X with macOS
|
||||||
|
|
||||||
|
## v1.4.2 / 2016-10-10
|
||||||
|
|
||||||
|
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
|
||||||
|
|
||||||
|
## v1.4.1 / 2016-10-04
|
||||||
|
|
||||||
|
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
|
||||||
|
|
||||||
|
## v1.4.0 / 2016-10-01
|
||||||
|
|
||||||
|
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
|
||||||
|
|
||||||
|
## v1.3.1 / 2016-06-28
|
||||||
|
|
||||||
|
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
|
||||||
|
|
||||||
|
## v1.3.0 / 2016-04-19
|
||||||
|
|
||||||
|
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
|
||||||
|
|
||||||
|
## v1.2.10 / 2016-03-02
|
||||||
|
|
||||||
|
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
|
||||||
|
|
||||||
|
## v1.2.9 / 2016-01-13
|
||||||
|
|
||||||
|
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
|
||||||
|
|
||||||
|
## v1.2.8 / 2015-12-17
|
||||||
|
|
||||||
|
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
|
||||||
|
* inotify: fix race in test
|
||||||
|
* enable race detection for continuous integration (Linux, Mac, Windows)
|
||||||
|
|
||||||
|
## v1.2.5 / 2015-10-17
|
||||||
|
|
||||||
|
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
|
||||||
|
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
|
||||||
|
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
|
||||||
|
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
|
||||||
|
|
||||||
|
## v1.2.1 / 2015-10-14
|
||||||
|
|
||||||
|
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
|
||||||
|
|
||||||
|
## v1.2.0 / 2015-02-08
|
||||||
|
|
||||||
|
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
|
||||||
|
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
|
||||||
|
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
|
||||||
|
|
||||||
|
## v1.1.1 / 2015-02-05
|
||||||
|
|
||||||
|
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
|
||||||
|
|
||||||
|
## v1.1.0 / 2014-12-12
|
||||||
|
|
||||||
|
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
|
||||||
|
* add low-level functions
|
||||||
|
* only need to store flags on directories
|
||||||
|
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
|
||||||
|
* done can be an unbuffered channel
|
||||||
|
* remove calls to os.NewSyscallError
|
||||||
|
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
|
||||||
|
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
|
||||||
|
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||||
|
|
||||||
|
## v1.0.4 / 2014-09-07
|
||||||
|
|
||||||
|
* kqueue: add dragonfly to the build tags.
|
||||||
|
* Rename source code files, rearrange code so exported APIs are at the top.
|
||||||
|
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
|
||||||
|
|
||||||
|
## v1.0.3 / 2014-08-19
|
||||||
|
|
||||||
|
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
|
||||||
|
|
||||||
|
## v1.0.2 / 2014-08-17
|
||||||
|
|
||||||
|
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||||
|
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
|
||||||
|
|
||||||
|
## v1.0.0 / 2014-08-15
|
||||||
|
|
||||||
|
* [API] Remove AddWatch on Windows, use Add.
|
||||||
|
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
|
||||||
|
* Minor updates based on feedback from golint.
|
||||||
|
|
||||||
|
## dev / 2014-07-09
|
||||||
|
|
||||||
|
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
|
||||||
|
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
|
||||||
|
|
||||||
|
## dev / 2014-07-04
|
||||||
|
|
||||||
|
* kqueue: fix incorrect mutex used in Close()
|
||||||
|
* Update example to demonstrate usage of Op.
|
||||||
|
|
||||||
|
## dev / 2014-06-28
|
||||||
|
|
||||||
|
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
|
||||||
|
* Fix for String() method on Event (thanks Alex Brainman)
|
||||||
|
* Don't build on Plan 9 or Solaris (thanks @4ad)
|
||||||
|
|
||||||
|
## dev / 2014-06-21
|
||||||
|
|
||||||
|
* Events channel of type Event rather than *Event.
|
||||||
|
* [internal] use syscall constants directly for inotify and kqueue.
|
||||||
|
* [internal] kqueue: rename events to kevents and fileEvent to event.
|
||||||
|
|
||||||
|
## dev / 2014-06-19
|
||||||
|
|
||||||
|
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
|
||||||
|
* [internal] remove cookie from Event struct (unused).
|
||||||
|
* [internal] Event struct has the same definition across every OS.
|
||||||
|
* [internal] remove internal watch and removeWatch methods.
|
||||||
|
|
||||||
|
## dev / 2014-06-12
|
||||||
|
|
||||||
|
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
|
||||||
|
* [API] Pluralized channel names: Events and Errors.
|
||||||
|
* [API] Renamed FileEvent struct to Event.
|
||||||
|
* [API] Op constants replace methods like IsCreate().
|
||||||
|
|
||||||
|
## dev / 2014-06-12
|
||||||
|
|
||||||
|
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||||
|
|
||||||
|
## dev / 2014-05-23
|
||||||
|
|
||||||
|
* [API] Remove current implementation of WatchFlags.
|
||||||
|
* current implementation doesn't take advantage of OS for efficiency
|
||||||
|
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
|
||||||
|
* no tests for the current implementation
|
||||||
|
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
|
||||||
|
|
||||||
|
## v0.9.3 / 2014-12-31
|
||||||
|
|
||||||
|
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||||
|
|
||||||
|
## v0.9.2 / 2014-08-17
|
||||||
|
|
||||||
|
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||||
|
|
||||||
|
## v0.9.1 / 2014-06-12
|
||||||
|
|
||||||
|
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||||
|
|
||||||
|
## 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 macOS [#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
|
77
src/runtime/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
Normal file
77
src/runtime/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
|
||||||
|
* Please indicate the platform you are using fsnotify on.
|
||||||
|
* A code example to reproduce the problem is appreciated.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
### Contributor License Agreement
|
||||||
|
|
||||||
|
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
|
||||||
|
|
||||||
|
Please indicate that you have signed the CLA in your pull request.
|
||||||
|
|
||||||
|
### How fsnotify is Developed
|
||||||
|
|
||||||
|
* Development is done on feature branches.
|
||||||
|
* Tests are run on BSD, Linux, macOS and Windows.
|
||||||
|
* Pull requests are reviewed and [applied to master][am] using [hub][].
|
||||||
|
* Maintainers may modify or squash commits rather than asking contributors to.
|
||||||
|
* To issue a new release, the maintainers will:
|
||||||
|
* Update the CHANGELOG
|
||||||
|
* Tag a version, which will become available through gopkg.in.
|
||||||
|
|
||||||
|
### How to Fork
|
||||||
|
|
||||||
|
For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
|
||||||
|
|
||||||
|
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
|
||||||
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
|
3. Ensure everything works and the tests pass (see below)
|
||||||
|
4. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
|
|
||||||
|
Contribute upstream:
|
||||||
|
|
||||||
|
1. Fork fsnotify on GitHub
|
||||||
|
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
|
||||||
|
3. Push to the branch (`git push fork my-new-feature`)
|
||||||
|
4. Create a new Pull Request on GitHub
|
||||||
|
|
||||||
|
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
|
||||||
|
|
||||||
|
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
|
||||||
|
|
||||||
|
To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
|
||||||
|
|
||||||
|
* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
|
||||||
|
* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
|
||||||
|
* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
|
||||||
|
* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
|
||||||
|
* When you're done, you will want to halt or destroy the Vagrant boxes.
|
||||||
|
|
||||||
|
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
|
||||||
|
|
||||||
|
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
|
||||||
|
|
||||||
|
### Maintainers
|
||||||
|
|
||||||
|
Help maintaining fsnotify is welcome. To be a maintainer:
|
||||||
|
|
||||||
|
* Submit a pull request and sign the CLA as above.
|
||||||
|
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
|
||||||
|
|
||||||
|
To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
|
||||||
|
|
||||||
|
All code changes should be internal pull requests.
|
||||||
|
|
||||||
|
Releases are tagged using [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
[hub]: https://github.com/github/hub
|
||||||
|
[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs
|
28
src/runtime/vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
Normal file
28
src/runtime/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.
|
130
src/runtime/vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
Normal file
130
src/runtime/vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# 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`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
done := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("event:", event)
|
||||||
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
log.Println("modified file:", event.Name)
|
||||||
|
}
|
||||||
|
case err, ok := <-watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = watcher.Add("/tmp/foo")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
src/runtime/vendor/github.com/fsnotify/fsnotify/fen.go
generated
vendored
Normal file
37
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
Normal file
68
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/go.mod
generated
vendored
Normal file
5
src/runtime/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
|
2
src/runtime/vendor/github.com/fsnotify/fsnotify/go.sum
generated
vendored
Normal file
2
src/runtime/vendor/github.com/fsnotify/fsnotify/go.sum
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
337
src/runtime/vendor/github.com/fsnotify/fsnotify/inotify.go
generated
vendored
Normal file
337
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
generated
vendored
Normal file
187
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/kqueue.go
generated
vendored
Normal file
521
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
generated
vendored
Normal file
11
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
generated
vendored
Normal file
12
src/runtime/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
src/runtime/vendor/github.com/fsnotify/fsnotify/windows.go
generated
vendored
Normal file
561
src/runtime/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
|
||||||
|
}
|
202
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/LICENSE-APACHE-2.0.txt
generated
vendored
202
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/LICENSE-APACHE-2.0.txt
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2015 xeipuuv
|
|
||||||
|
|
||||||
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.
|
|
41
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/README.md
generated
vendored
41
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/README.md
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
# gojsonpointer
|
|
||||||
An implementation of JSON Pointer - Go language
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
jsonText := `{
|
|
||||||
"name": "Bobby B",
|
|
||||||
"occupation": {
|
|
||||||
"title" : "King",
|
|
||||||
"years" : 15,
|
|
||||||
"heir" : "Joffrey B"
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var jsonDocument map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(jsonText), &jsonDocument)
|
|
||||||
|
|
||||||
//create a JSON pointer
|
|
||||||
pointerString := "/occupation/title"
|
|
||||||
pointer, _ := NewJsonPointer(pointerString)
|
|
||||||
|
|
||||||
//SET a new value for the "title" in the document
|
|
||||||
pointer.Set(jsonDocument, "Supreme Leader of Westeros")
|
|
||||||
|
|
||||||
//GET the new "title" from the document
|
|
||||||
title, _, _ := pointer.Get(jsonDocument)
|
|
||||||
fmt.Println(title) //outputs "Supreme Leader of Westeros"
|
|
||||||
|
|
||||||
//DELETE the "heir" from the document
|
|
||||||
deletePointer := NewJsonPointer("/occupation/heir")
|
|
||||||
deletePointer.Delete(jsonDocument)
|
|
||||||
|
|
||||||
b, _ := json.Marshal(jsonDocument)
|
|
||||||
fmt.Println(string(b))
|
|
||||||
//outputs `{"name":"Bobby B","occupation":{"title":"Supreme Leader of Westeros","years":15}}`
|
|
||||||
|
|
||||||
|
|
||||||
## References
|
|
||||||
https://tools.ietf.org/html/rfc6901
|
|
||||||
|
|
||||||
### Note
|
|
||||||
The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented.
|
|
211
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
211
src/runtime/vendor/github.com/xeipuuv/gojsonpointer/pointer.go
generated
vendored
@ -1,211 +0,0 @@
|
|||||||
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// author xeipuuv
|
|
||||||
// author-github https://github.com/xeipuuv
|
|
||||||
// author-mail xeipuuv@gmail.com
|
|
||||||
//
|
|
||||||
// repository-name gojsonpointer
|
|
||||||
// repository-desc An implementation of JSON Pointer - Go language
|
|
||||||
//
|
|
||||||
// description Main and unique file.
|
|
||||||
//
|
|
||||||
// created 25-02-2013
|
|
||||||
|
|
||||||
package gojsonpointer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
const_empty_pointer = ``
|
|
||||||
const_pointer_separator = `/`
|
|
||||||
|
|
||||||
const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
|
|
||||||
)
|
|
||||||
|
|
||||||
type implStruct struct {
|
|
||||||
mode string // "SET" or "GET"
|
|
||||||
|
|
||||||
inDocument interface{}
|
|
||||||
|
|
||||||
setInValue interface{}
|
|
||||||
|
|
||||||
getOutNode interface{}
|
|
||||||
getOutKind reflect.Kind
|
|
||||||
outError error
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonPointer struct {
|
|
||||||
referenceTokens []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewJsonPointer parses the given string JSON pointer and returns an object
|
|
||||||
func NewJsonPointer(jsonPointerString string) (p JsonPointer, err error) {
|
|
||||||
|
|
||||||
// Pointer to the root of the document
|
|
||||||
if len(jsonPointerString) == 0 {
|
|
||||||
// Keep referenceTokens nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if jsonPointerString[0] != '/' {
|
|
||||||
return p, errors.New(const_invalid_start)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.referenceTokens = strings.Split(jsonPointerString[1:], const_pointer_separator)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to retrieve a value from a JSON document
|
|
||||||
func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
|
|
||||||
|
|
||||||
is := &implStruct{mode: "GET", inDocument: document}
|
|
||||||
p.implementation(is)
|
|
||||||
return is.getOutNode, is.getOutKind, is.outError
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to update a value from a JSON document
|
|
||||||
func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
|
|
||||||
|
|
||||||
is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
|
|
||||||
p.implementation(is)
|
|
||||||
return document, is.outError
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses the pointer to delete a value from a JSON document
|
|
||||||
func (p *JsonPointer) Delete(document interface{}) (interface{}, error) {
|
|
||||||
is := &implStruct{mode: "DEL", inDocument: document}
|
|
||||||
p.implementation(is)
|
|
||||||
return document, is.outError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both Get and Set functions use the same implementation to avoid code duplication
|
|
||||||
func (p *JsonPointer) implementation(i *implStruct) {
|
|
||||||
|
|
||||||
kind := reflect.Invalid
|
|
||||||
|
|
||||||
// Full document when empty
|
|
||||||
if len(p.referenceTokens) == 0 {
|
|
||||||
i.getOutNode = i.inDocument
|
|
||||||
i.outError = nil
|
|
||||||
i.getOutKind = kind
|
|
||||||
i.outError = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node := i.inDocument
|
|
||||||
|
|
||||||
previousNodes := make([]interface{}, len(p.referenceTokens))
|
|
||||||
previousTokens := make([]string, len(p.referenceTokens))
|
|
||||||
|
|
||||||
for ti, token := range p.referenceTokens {
|
|
||||||
|
|
||||||
isLastToken := ti == len(p.referenceTokens)-1
|
|
||||||
previousNodes[ti] = node
|
|
||||||
previousTokens[ti] = token
|
|
||||||
|
|
||||||
switch v := node.(type) {
|
|
||||||
|
|
||||||
case map[string]interface{}:
|
|
||||||
decodedToken := decodeReferenceToken(token)
|
|
||||||
if _, ok := v[decodedToken]; ok {
|
|
||||||
node = v[decodedToken]
|
|
||||||
if isLastToken && i.mode == "SET" {
|
|
||||||
v[decodedToken] = i.setInValue
|
|
||||||
} else if isLastToken && i.mode == "DEL" {
|
|
||||||
delete(v, decodedToken)
|
|
||||||
}
|
|
||||||
} else if isLastToken && i.mode == "SET" {
|
|
||||||
v[decodedToken] = i.setInValue
|
|
||||||
} else {
|
|
||||||
i.outError = fmt.Errorf("Object has no key '%s'", decodedToken)
|
|
||||||
i.getOutKind = reflect.Map
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case []interface{}:
|
|
||||||
tokenIndex, err := strconv.Atoi(token)
|
|
||||||
if err != nil {
|
|
||||||
i.outError = fmt.Errorf("Invalid array index '%s'", token)
|
|
||||||
i.getOutKind = reflect.Slice
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if tokenIndex < 0 || tokenIndex >= len(v) {
|
|
||||||
i.outError = fmt.Errorf("Out of bound array[0,%d] index '%d'", len(v), tokenIndex)
|
|
||||||
i.getOutKind = reflect.Slice
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node = v[tokenIndex]
|
|
||||||
if isLastToken && i.mode == "SET" {
|
|
||||||
v[tokenIndex] = i.setInValue
|
|
||||||
} else if isLastToken && i.mode == "DEL" {
|
|
||||||
v[tokenIndex] = v[len(v)-1]
|
|
||||||
v[len(v)-1] = nil
|
|
||||||
v = v[:len(v)-1]
|
|
||||||
previousNodes[ti-1].(map[string]interface{})[previousTokens[ti-1]] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
i.outError = fmt.Errorf("Invalid token reference '%s'", token)
|
|
||||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
|
||||||
i.getOutNode = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i.getOutNode = node
|
|
||||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
|
||||||
i.outError = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pointer to string representation function
|
|
||||||
func (p *JsonPointer) String() string {
|
|
||||||
|
|
||||||
if len(p.referenceTokens) == 0 {
|
|
||||||
return const_empty_pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
|
|
||||||
|
|
||||||
return pointerString
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific JSON pointer encoding here
|
|
||||||
// ~0 => ~
|
|
||||||
// ~1 => /
|
|
||||||
// ... and vice versa
|
|
||||||
|
|
||||||
func decodeReferenceToken(token string) string {
|
|
||||||
step1 := strings.Replace(token, `~1`, `/`, -1)
|
|
||||||
step2 := strings.Replace(step1, `~0`, `~`, -1)
|
|
||||||
return step2
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeReferenceToken(token string) string {
|
|
||||||
step1 := strings.Replace(token, `~`, `~0`, -1)
|
|
||||||
step2 := strings.Replace(step1, `/`, `~1`, -1)
|
|
||||||
return step2
|
|
||||||
}
|
|
6
src/runtime/vendor/modules.txt
vendored
6
src/runtime/vendor/modules.txt
vendored
@ -115,6 +115,9 @@ github.com/cri-o/cri-o/pkg/annotations
|
|||||||
github.com/davecgh/go-spew/spew
|
github.com/davecgh/go-spew/spew
|
||||||
# github.com/docker/go-units v0.4.0
|
# github.com/docker/go-units v0.4.0
|
||||||
github.com/docker/go-units
|
github.com/docker/go-units
|
||||||
|
# github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
## explicit
|
||||||
|
github.com/fsnotify/fsnotify
|
||||||
# github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
# github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||||
github.com/globalsign/mgo/bson
|
github.com/globalsign/mgo/bson
|
||||||
github.com/globalsign/mgo/internal/json
|
github.com/globalsign/mgo/internal/json
|
||||||
@ -260,9 +263,6 @@ github.com/vishvananda/netlink/nl
|
|||||||
# github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
# github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
||||||
## explicit
|
## explicit
|
||||||
github.com/vishvananda/netns
|
github.com/vishvananda/netns
|
||||||
# github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb
|
|
||||||
## explicit
|
|
||||||
github.com/xeipuuv/gojsonpointer
|
|
||||||
# go.opencensus.io v0.22.4
|
# go.opencensus.io v0.22.4
|
||||||
go.opencensus.io
|
go.opencensus.io
|
||||||
go.opencensus.io/internal
|
go.opencensus.io/internal
|
||||||
|
Loading…
Reference in New Issue
Block a user