clean up unused pacakges
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Jul 29, 2015 at 09:46 UTC
64960110f6435d9b23dd9dbdd4838051d40f6a28
49 files changed
-5108
Godeps/Godeps.json
-13
@@ -14,10 +14,6 @@
14
"Comment": "null-5",
15
"Rev": "75cd24fc2f2c2a2088577d12123ddee5f54e0675"
16
},
17
- {
18
- "ImportPath": "github.com/ActiveState/tail",
19
- "Rev": "068b72961a6bc5b4a82cf4fc14ccc724c0cfa73a"
20
- },
17
{
18
"ImportPath": "github.com/Sirupsen/logrus",
19
"Comment": "v0.7.3-2-g26709e2",
@@ -111,11 +107,6 @@
107
"ImportPath": "github.com/hashicorp/yamux",
108
"Rev": "9feabe6854fadca1abec9cd3bd2a613fe9a34000"
109
},
114
- {
115
- "ImportPath": "github.com/howeyc/fsnotify",
116
- "Comment": "v0.9.0-11-g6b1ef89",
117
- "Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
118
- },
110
{
111
"ImportPath": "github.com/huin/goupnp",
112
"Rev": "223008361153d7d434c1f0ac990cd3fcae6931f5"
@@ -327,10 +318,6 @@
318
"ImportPath": "gopkg.in/fsnotify.v1",
319
"Comment": "v1.2.0",
320
"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
330
- },
331
- {
332
- "ImportPath": "gopkg.in/tomb.v1",
333
- "Rev": "dd632973f1e7218eb1089048e0798ec9ae7dceb8"
321
}
322
]
323
}
Godeps/_workspace/src/github.com/ActiveState/tail/.gitignore
deleted
-3
@@ -1,3 +0,0 @@
1
-.test
2
-.go
3
-
Godeps/_workspace/src/github.com/ActiveState/tail/.travis.yml
deleted
-10
@@ -1,10 +0,0 @@
1
-language: go
2
-
3
-# depman needs to be installed with GOPATH pointing to a single directory.
4
-script:
5
- - GOPATH=$HOME/gopath go get github.com/vube/depman
6
- - GOPATH=$HOME/gopath $HOME/gopath/bin/depman
7
- - go test -v ./...
8
-
9
-go:
10
- - 1.2.1
Godeps/_workspace/src/github.com/ActiveState/tail/CHANGES.md
deleted
-44
@@ -1,44 +0,0 @@
1
-# July, 2014
2
-
3
-* Fix tail for Windows (PR #36)
4
-
5
-# May, 2014
6
-
7
-* Improved rate limiting using leaky bucket (PR #29)
8
-* Fix odd line splitting (PR #30)
9
-
10
-# Apr, 2014
11
-
12
-* LimitRate now discards read buffer (PR #28)
13
-* allow reading of longer lines if MaxLineSize is unset (PR #24)
14
-* updated deps.json to latest fsnotify (441bbc86b1)
15
-
16
-# Feb, 2014
17
-
18
-* added `Config.Logger` to suppress library logging
19
-
20
-# Nov, 2013
21
-
22
-* add Cleanup to remove leaky inotify watches (PR #20)
23
-
24
-# Aug, 2013
25
-
26
-* redesigned Location field (PR #12)
27
-* add tail.Tell (PR #14)
28
-
29
-# July, 2013
30
-
31
-* Rate limiting (PR #10)
32
-
33
-# May, 2013
34
-
35
-* Detect file deletions/renames in polling file watcher (PR #1)
36
-* Detect file truncation
37
-* Fix potential race condition when reopening the file (issue 5)
38
-* Fix potential blocking of `tail.Stop` (issue 4)
39
-* Fix uncleaned up ChangeEvents goroutines after calling tail.Stop
40
-* Support Follow=false
41
-
42
-# Feb, 2013
43
-
44
-* Initial open source release
Godeps/_workspace/src/github.com/ActiveState/tail/Dockerfile
deleted
-27
@@ -1,27 +0,0 @@
1
-FROM ubuntu
2
-
3
-RUN apt-get -qy update
4
-RUN apt-get -y install golang-go
5
-RUN apt-get -y install git
6
-RUN apt-get -y install mercurial subversion
7
-
8
-ENV GOPATH $HOME/go
9
-
10
-RUN mkdir -p $GOPATH/src/github.com/ActiveState/tail/
11
-ADD . $GOPATH/src/github.com/ActiveState/tail/
12
-
13
-# expecting to fetch dependencies successfully.
14
-RUN go get -v github.com/ActiveState/tail
15
-
16
-# expecting to run the test successfully.
17
-RUN go test -v github.com/ActiveState/tail
18
-
19
-# expecting to install successfully
20
-RUN go install -v github.com/ActiveState/tail
21
-RUN go install -v github.com/ActiveState/tail/cmd/gotail
22
-
23
-RUN $GOPATH/bin/gotail -h || true
24
-
25
-ENV PATH $GOPATH/bin:$PATH
26
-CMD ["gotail"]
27
-
Godeps/_workspace/src/github.com/ActiveState/tail/LICENSE.txt
deleted
-23
@@ -1,23 +0,0 @@
1
-# This is the MIT license
2
-
3
-# Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
4
-
5
-Permission is hereby granted, free of charge, to any person obtaining a
6
-copy of this software and associated documentation files (the
7
-"Software"), to deal in the Software without restriction, including
8
-without limitation the rights to use, copy, modify, merge, publish,
9
-distribute, sublicense, and/or sell copies of the Software, and to
10
-permit persons to whom the Software is furnished to do so, subject to
11
-the following conditions:
12
-
13
-The above copyright notice and this permission notice shall be included
14
-in all copies or substantial portions of the Software.
15
-
16
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
-
Godeps/_workspace/src/github.com/ActiveState/tail/Makefile
deleted
-11
@@ -1,11 +0,0 @@
1
-default: test
2
-
3
-test: *.go
4
- go test -v ./...
5
-
6
-fmt:
7
- gofmt -w .
8
-
9
-# Run the test in an isolated environment.
10
-fulltest:
11
- docker build -t ActiveState/tail .
Godeps/_workspace/src/github.com/ActiveState/tail/README.md
deleted
-24
@@ -1,24 +0,0 @@
1
-[](https://travis-ci.org/ActiveState/tail)
2
-
3
-# Go package for tail-ing files
4
-
5
-A Go package striving to emulate the features of the BSD `tail` program.
6
-
7
-```Go
8
-t, err := tail.TailFile("/var/log/nginx.log", tail.Config{Follow: true})
9
-for line := range t.Lines {
10
- fmt.Println(line.Text)
11
-}
12
-```
13
-
14
-See [API documentation](http://godoc.org/github.com/ActiveState/tail).
15
-
16
-## Log rotation
17
-
18
-Tail comes with full support for truncation/move detection as it is
19
-designed to work with log rotation tools.
20
-
21
-## Installing
22
-
23
- go get github.com/ActiveState/tail/...
24
-
Godeps/_workspace/src/github.com/ActiveState/tail/cmd/gotail/.gitignore
deleted
-1
@@ -1 +0,0 @@
1
-gotail
Godeps/_workspace/src/github.com/ActiveState/tail/cmd/gotail/Makefile
deleted
-4
@@ -1,4 +0,0 @@
1
-default: gotail
2
-
3
-gotail: *.go ../../*.go
4
- go build
Godeps/_workspace/src/github.com/ActiveState/tail/cmd/gotail/gotail.go
deleted
-64
@@ -1,64 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package main
4
-
5
-import (
6
- "flag"
7
- "fmt"
8
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail"
9
- "os"
10
-)
11
-
12
-func args2config() (tail.Config, int64) {
13
- config := tail.Config{Follow: true}
14
- n := int64(0)
15
- maxlinesize := int(0)
16
- flag.Int64Var(&n, "n", 0, "tail from the last Nth location")
17
- flag.IntVar(&maxlinesize, "max", 0, "max line size")
18
- flag.BoolVar(&config.Follow, "f", false, "wait for additional data to be appended to the file")
19
- flag.BoolVar(&config.ReOpen, "F", false, "follow, and track file rename/rotation")
20
- flag.BoolVar(&config.Poll, "p", false, "use polling, instead of inotify")
21
- flag.Parse()
22
- if config.ReOpen {
23
- config.Follow = true
24
- }
25
- config.MaxLineSize = maxlinesize
26
- return config, n
27
-}
28
-
29
-func main() {
30
- config, n := args2config()
31
- if flag.NFlag() < 1 {
32
- fmt.Println("need one or more files as arguments")
33
- os.Exit(1)
34
- }
35
-
36
- if n != 0 {
37
- config.Location = &tail.SeekInfo{-n, os.SEEK_END}
38
- }
39
-
40
- done := make(chan bool)
41
- for _, filename := range flag.Args() {
42
- go tailFile(filename, config, done)
43
- }
44
-
45
- for _, _ = range flag.Args() {
46
- <-done
47
- }
48
-}
49
-
50
-func tailFile(filename string, config tail.Config, done chan bool) {
51
- defer func() { done <- true }()
52
- t, err := tail.TailFile(filename, config)
53
- if err != nil {
54
- fmt.Println(err)
55
- return
56
- }
57
- for line := range t.Lines {
58
- fmt.Println(line.Text)
59
- }
60
- err = t.Wait()
61
- if err != nil {
62
- fmt.Println(err)
63
- }
64
-}
Godeps/_workspace/src/github.com/ActiveState/tail/deps.json
deleted
-14
@@ -1,14 +0,0 @@
1
-{
2
- "github.com/howeyc/fsnotify": {
3
- "repo": "http://github.com/howeyc/fsnotify.git",
4
- "version": "441bbc86b167",
5
- "type": "git-clone",
6
- "alias": "github.com/howeyc/fsnotify"
7
- },
8
- "gopkg.in/tomb.v1": {
9
- "repo": "https://github.com/go-tomb/tomb.git",
10
- "version": "c131134a1947e9afd9cecfe11f4c6dff0732ae58",
11
- "type": "git-clone",
12
- "alias": "gopkg.in/tomb.v1"
13
- }
14
-}
Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter/Licence
deleted
-7
@@ -1,7 +0,0 @@
1
-Copyright (C) 2013 99designs
2
-
3
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
-
5
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
-
7
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter/leakybucket.go
deleted
-97
@@ -1,97 +0,0 @@
1
-// Package ratelimiter implements the Leaky Bucket ratelimiting algorithm with memcached and in-memory backends.
2
-package ratelimiter
3
-
4
-import (
5
- "time"
6
-)
7
-
8
-type LeakyBucket struct {
9
- Size uint16
10
- Fill float64
11
- LeakInterval time.Duration // time.Duration for 1 unit of size to leak
12
- Lastupdate time.Time
13
- Now func() time.Time
14
-}
15
-
16
-func NewLeakyBucket(size uint16, leakInterval time.Duration) *LeakyBucket {
17
- bucket := LeakyBucket{
18
- Size: size,
19
- Fill: 0,
20
- LeakInterval: leakInterval,
21
- Now: time.Now,
22
- Lastupdate: time.Now(),
23
- }
24
-
25
- return &bucket
26
-}
27
-
28
-func (b *LeakyBucket) updateFill() {
29
- now := b.Now()
30
- if b.Fill > 0 {
31
- elapsed := now.Sub(b.Lastupdate)
32
-
33
- b.Fill -= float64(elapsed) / float64(b.LeakInterval)
34
- if b.Fill < 0 {
35
- b.Fill = 0
36
- }
37
- }
38
- b.Lastupdate = now
39
-}
40
-
41
-func (b *LeakyBucket) Pour(amount uint16) bool {
42
- b.updateFill()
43
-
44
- var newfill float64 = b.Fill + float64(amount)
45
-
46
- if newfill > float64(b.Size) {
47
- return false
48
- }
49
-
50
- b.Fill = newfill
51
-
52
- return true
53
-}
54
-
55
-// The time at which this bucket will be completely drained
56
-func (b *LeakyBucket) DrainedAt() time.Time {
57
- return b.Lastupdate.Add(time.Duration(b.Fill * float64(b.LeakInterval)))
58
-}
59
-
60
-// The duration until this bucket is completely drained
61
-func (b *LeakyBucket) TimeToDrain() time.Duration {
62
- return b.DrainedAt().Sub(b.Now())
63
-}
64
-
65
-func (b *LeakyBucket) TimeSinceLastUpdate() time.Duration {
66
- return b.Now().Sub(b.Lastupdate)
67
-}
68
-
69
-type LeakyBucketSer struct {
70
- Size uint16
71
- Fill float64
72
- LeakInterval time.Duration // time.Duration for 1 unit of size to leak
73
- Lastupdate time.Time
74
-}
75
-
76
-func (b *LeakyBucket) Serialise() *LeakyBucketSer {
77
- bucket := LeakyBucketSer{
78
- Size: b.Size,
79
- Fill: b.Fill,
80
- LeakInterval: b.LeakInterval,
81
- Lastupdate: b.Lastupdate,
82
- }
83
-
84
- return &bucket
85
-}
86
-
87
-func (b *LeakyBucketSer) DeSerialise() *LeakyBucket {
88
- bucket := LeakyBucket{
89
- Size: b.Size,
90
- Fill: b.Fill,
91
- LeakInterval: b.LeakInterval,
92
- Lastupdate: b.Lastupdate,
93
- Now: time.Now,
94
- }
95
-
96
- return &bucket
97
-}
Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter/leakybucket_test.go
deleted
-73
@@ -1,73 +0,0 @@
1
-package ratelimiter
2
-
3
-import (
4
- "testing"
5
- "time"
6
-)
7
-
8
-func TestPour(t *testing.T) {
9
- bucket := NewLeakyBucket(60, time.Second)
10
- bucket.Lastupdate = time.Unix(0, 0)
11
-
12
- bucket.Now = func() time.Time { return time.Unix(1, 0) }
13
-
14
- if bucket.Pour(61) {
15
- t.Error("Expected false")
16
- }
17
-
18
- if !bucket.Pour(10) {
19
- t.Error("Expected true")
20
- }
21
-
22
- if !bucket.Pour(49) {
23
- t.Error("Expected true")
24
- }
25
-
26
- if bucket.Pour(2) {
27
- t.Error("Expected false")
28
- }
29
-
30
- bucket.Now = func() time.Time { return time.Unix(61, 0) }
31
- if !bucket.Pour(60) {
32
- t.Error("Expected true")
33
- }
34
-
35
- if bucket.Pour(1) {
36
- t.Error("Expected false")
37
- }
38
-
39
- bucket.Now = func() time.Time { return time.Unix(70, 0) }
40
-
41
- if !bucket.Pour(1) {
42
- t.Error("Expected true")
43
- }
44
-
45
-}
46
-
47
-func TestTimeSinceLastUpdate(t *testing.T) {
48
- bucket := NewLeakyBucket(60, time.Second)
49
- bucket.Now = func() time.Time { return time.Unix(1, 0) }
50
- bucket.Pour(1)
51
- bucket.Now = func() time.Time { return time.Unix(2, 0) }
52
-
53
- sinceLast := bucket.TimeSinceLastUpdate()
54
- if sinceLast != time.Second*1 {
55
- t.Errorf("Expected time since last update to be less than 1 second, got %d", sinceLast)
56
- }
57
-}
58
-
59
-func TestTimeToDrain(t *testing.T) {
60
- bucket := NewLeakyBucket(60, time.Second)
61
- bucket.Now = func() time.Time { return time.Unix(1, 0) }
62
- bucket.Pour(10)
63
-
64
- if bucket.TimeToDrain() != time.Second*10 {
65
- t.Error("Time to drain should be 10 seconds")
66
- }
67
-
68
- bucket.Now = func() time.Time { return time.Unix(2, 0) }
69
-
70
- if bucket.TimeToDrain() != time.Second*9 {
71
- t.Error("Time to drain should be 9 seconds")
72
- }
73
-}
Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter/memory.go
deleted
-58
@@ -1,58 +0,0 @@
1
-package ratelimiter
2
-
3
-import (
4
- "errors"
5
- "time"
6
-)
7
-
8
-const GC_SIZE int = 100
9
-
10
-type Memory struct {
11
- store map[string]LeakyBucket
12
- lastGCCollected time.Time
13
-}
14
-
15
-func NewMemory() *Memory {
16
- m := new(Memory)
17
- m.store = make(map[string]LeakyBucket)
18
- m.lastGCCollected = time.Now()
19
- return m
20
-}
21
-
22
-func (m *Memory) GetBucketFor(key string) (*LeakyBucket, error) {
23
-
24
- bucket, ok := m.store[key]
25
- if !ok {
26
- return nil, errors.New("miss")
27
- }
28
-
29
- return &bucket, nil
30
-}
31
-
32
-func (m *Memory) SetBucketFor(key string, bucket LeakyBucket) error {
33
-
34
- if len(m.store) > GC_SIZE {
35
- m.GarbageCollect()
36
- }
37
-
38
- m.store[key] = bucket
39
-
40
- return nil
41
-}
42
-
43
-func (m *Memory) GarbageCollect() {
44
- now := time.Now()
45
-
46
- // rate limit GC to once per minute
47
- if now.Add(60*time.Second).Unix() > m.lastGCCollected.Unix() {
48
-
49
- for key, bucket := range m.store {
50
- // if the bucket is drained, then GC
51
- if bucket.DrainedAt().Unix() > now.Unix() {
52
- delete(m.store, key)
53
- }
54
- }
55
-
56
- m.lastGCCollected = now
57
- }
58
-}
Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter/storage.go
deleted
-6
@@ -1,6 +0,0 @@
1
-package ratelimiter
2
-
3
-type Storage interface {
4
- GetBucketFor(string) (*LeakyBucket, error)
5
- SetBucketFor(string, LeakyBucket) error
6
-}
Godeps/_workspace/src/github.com/ActiveState/tail/tail.go
deleted
-361
@@ -1,361 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package tail
4
-
5
-import (
6
- "bufio"
7
- "fmt"
8
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter"
9
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/util"
10
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/watch"
11
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/tomb.v1"
12
- "io"
13
- "io/ioutil"
14
- "log"
15
- "os"
16
- "strings"
17
- "time"
18
-)
19
-
20
-var (
21
- ErrStop = fmt.Errorf("tail should now stop")
22
-)
23
-
24
-type Line struct {
25
- Text string
26
- Time time.Time
27
- Err error // Error from tail
28
-}
29
-
30
-// NewLine returns a Line with present time.
31
-func NewLine(text string) *Line {
32
- return &Line{text, time.Now(), nil}
33
-}
34
-
35
-// SeekInfo represents arguments to `os.Seek`
36
-type SeekInfo struct {
37
- Offset int64
38
- Whence int // os.SEEK_*
39
-}
40
-
41
-// Config is used to specify how a file must be tailed.
42
-type Config struct {
43
- // File-specifc
44
- Location *SeekInfo // Seek to this location before tailing
45
- ReOpen bool // Reopen recreated files (tail -F)
46
- MustExist bool // Fail early if the file does not exist
47
- Poll bool // Poll for file changes instead of using inotify
48
- RateLimiter *ratelimiter.LeakyBucket
49
-
50
- // Generic IO
51
- Follow bool // Continue looking for new lines (tail -f)
52
- MaxLineSize int // If non-zero, split longer lines into multiple lines
53
-
54
- // Logger, when nil, is set to tail.DefaultLogger
55
- // To disable logging: set field to tail.DiscardingLogger
56
- Logger *log.Logger
57
-}
58
-
59
-type Tail struct {
60
- Filename string
61
- Lines chan *Line
62
- Config
63
-
64
- file *os.File
65
- reader *bufio.Reader
66
- watcher watch.FileWatcher
67
- changes *watch.FileChanges
68
-
69
- tomb.Tomb // provides: Done, Kill, Dying
70
-}
71
-
72
-var (
73
- // DefaultLogger is used when Config.Logger == nil
74
- DefaultLogger = log.New(os.Stderr, "", log.LstdFlags)
75
- // DiscardingLogger can be used to disable logging output
76
- DiscardingLogger = log.New(ioutil.Discard, "", 0)
77
-)
78
-
79
-// TailFile begins tailing the file. Output stream is made available
80
-// via the `Tail.Lines` channel. To handle errors during tailing,
81
-// invoke the `Wait` or `Err` method after finishing reading from the
82
-// `Lines` channel.
83
-func TailFile(filename string, config Config) (*Tail, error) {
84
- if config.ReOpen && !config.Follow {
85
- util.Fatal("cannot set ReOpen without Follow.")
86
- }
87
-
88
- t := &Tail{
89
- Filename: filename,
90
- Lines: make(chan *Line),
91
- Config: config,
92
- }
93
-
94
- // when Logger was not specified in config, use default logger
95
- if t.Logger == nil {
96
- t.Logger = log.New(os.Stderr, "", log.LstdFlags)
97
- }
98
-
99
- if t.Poll {
100
- t.watcher = watch.NewPollingFileWatcher(filename)
101
- } else {
102
- t.watcher = watch.NewInotifyFileWatcher(filename)
103
- }
104
-
105
- if t.MustExist {
106
- var err error
107
- t.file, err = OpenFile(t.Filename)
108
- if err != nil {
109
- return nil, err
110
- }
111
- }
112
-
113
- go t.tailFileSync()
114
-
115
- return t, nil
116
-}
117
-
118
-// Return the file's current position, like stdio's ftell().
119
-// But this value is not very accurate.
120
-// it may readed one line in the chan(tail.Lines),
121
-// so it may lost one line.
122
-func (tail *Tail) Tell() (offset int64, err error) {
123
- if tail.file == nil {
124
- return
125
- }
126
- offset, err = tail.file.Seek(0, os.SEEK_CUR)
127
- if err == nil {
128
- offset -= int64(tail.reader.Buffered())
129
- }
130
- return
131
-}
132
-
133
-// Stop stops the tailing activity.
134
-func (tail *Tail) Stop() error {
135
- tail.Kill(nil)
136
- return tail.Wait()
137
-}
138
-
139
-func (tail *Tail) close() {
140
- close(tail.Lines)
141
- if tail.file != nil {
142
- tail.file.Close()
143
- }
144
-}
145
-
146
-func (tail *Tail) reopen() error {
147
- if tail.file != nil {
148
- tail.file.Close()
149
- }
150
- for {
151
- var err error
152
- tail.file, err = OpenFile(tail.Filename)
153
- if err != nil {
154
- if os.IsNotExist(err) {
155
- tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)
156
- if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil {
157
- if err == tomb.ErrDying {
158
- return err
159
- }
160
- return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
161
- }
162
- continue
163
- }
164
- return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err)
165
- }
166
- break
167
- }
168
- return nil
169
-}
170
-
171
-func (tail *Tail) readLine() (string, error) {
172
- line, err := tail.reader.ReadString('\n')
173
- if err != nil {
174
- // Note ReadString "returns the data read before the error" in
175
- // case of an error, including EOF, so we return it as is. The
176
- // caller is expected to process it if err is EOF.
177
- return line, err
178
- }
179
-
180
- line = strings.TrimRight(line, "\n")
181
-
182
- return line, err
183
-}
184
-
185
-func (tail *Tail) tailFileSync() {
186
- defer tail.Done()
187
- defer tail.close()
188
-
189
- if !tail.MustExist {
190
- // deferred first open.
191
- err := tail.reopen()
192
- if err != nil {
193
- if err != tomb.ErrDying {
194
- tail.Kill(err)
195
- }
196
- return
197
- }
198
- }
199
-
200
- // Seek to requested location on first open of the file.
201
- if tail.Location != nil {
202
- _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence)
203
- tail.Logger.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location)
204
- if err != nil {
205
- tail.Killf("Seek error on %s: %s", tail.Filename, err)
206
- return
207
- }
208
- }
209
-
210
- tail.openReader()
211
-
212
- // Read line by line.
213
- for {
214
- line, err := tail.readLine()
215
-
216
- // Process `line` even if err is EOF.
217
- if err == nil || (err == io.EOF && line != "") {
218
- cooloff := !tail.sendLine(line)
219
- if cooloff {
220
- // Wait a second before seeking till the end of
221
- // file when rate limit is reached.
222
- msg := fmt.Sprintf(
223
- "Too much log activity; waiting a second " +
224
- "before resuming tailing")
225
- tail.Lines <- &Line{msg, time.Now(), fmt.Errorf(msg)}
226
- select {
227
- case <-time.After(time.Second):
228
- case <-tail.Dying():
229
- return
230
- }
231
- err = tail.seekEnd()
232
- if err != nil {
233
- tail.Kill(err)
234
- return
235
- }
236
- }
237
- } else if err == io.EOF {
238
- if !tail.Follow {
239
- return
240
- }
241
- // When EOF is reached, wait for more data to become
242
- // available. Wait strategy is based on the `tail.watcher`
243
- // implementation (inotify or polling).
244
- err := tail.waitForChanges()
245
- if err != nil {
246
- if err != ErrStop {
247
- tail.Kill(err)
248
- }
249
- return
250
- }
251
- } else {
252
- // non-EOF error
253
- tail.Killf("Error reading %s: %s", tail.Filename, err)
254
- return
255
- }
256
-
257
- select {
258
- case <-tail.Dying():
259
- return
260
- default:
261
- }
262
- }
263
-}
264
-
265
-// waitForChanges waits until the file has been appended, deleted,
266
-// moved or truncated. When moved or deleted - the file will be
267
-// reopened if ReOpen is true. Truncated files are always reopened.
268
-func (tail *Tail) waitForChanges() error {
269
- if tail.changes == nil {
270
- st, err := tail.file.Stat()
271
- if err != nil {
272
- return err
273
- }
274
- tail.changes = tail.watcher.ChangeEvents(&tail.Tomb, st)
275
- }
276
-
277
- select {
278
- case <-tail.changes.Modified:
279
- return nil
280
- case <-tail.changes.Deleted:
281
- tail.changes = nil
282
- if tail.ReOpen {
283
- // XXX: we must not log from a library.
284
- tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename)
285
- if err := tail.reopen(); err != nil {
286
- return err
287
- }
288
- tail.Logger.Printf("Successfully reopened %s", tail.Filename)
289
- tail.openReader()
290
- return nil
291
- } else {
292
- tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename)
293
- return ErrStop
294
- }
295
- case <-tail.changes.Truncated:
296
- // Always reopen truncated files (Follow is true)
297
- tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename)
298
- if err := tail.reopen(); err != nil {
299
- return err
300
- }
301
- tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
302
- tail.openReader()
303
- return nil
304
- case <-tail.Dying():
305
- return ErrStop
306
- }
307
- panic("unreachable")
308
-}
309
-
310
-func (tail *Tail) openReader() {
311
- if tail.MaxLineSize > 0 {
312
- // add 2 to account for newline characters
313
- tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
314
- } else {
315
- tail.reader = bufio.NewReader(tail.file)
316
- }
317
-}
318
-
319
-func (tail *Tail) seekEnd() error {
320
- _, err := tail.file.Seek(0, 2)
321
- if err != nil {
322
- return fmt.Errorf("Seek error on %s: %s", tail.Filename, err)
323
- }
324
- // Reset the read buffer whenever the file is re-seek'ed
325
- tail.reader.Reset(tail.file)
326
- return nil
327
-}
328
-
329
-// sendLine sends the line(s) to Lines channel, splitting longer lines
330
-// if necessary. Return false if rate limit is reached.
331
-func (tail *Tail) sendLine(line string) bool {
332
- now := time.Now()
333
- lines := []string{line}
334
-
335
- // Split longer lines
336
- if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize {
337
- lines = util.PartitionString(line, tail.MaxLineSize)
338
- }
339
-
340
- for _, line := range lines {
341
- tail.Lines <- &Line{line, now, nil}
342
- }
343
-
344
- if tail.Config.RateLimiter != nil {
345
- ok := tail.Config.RateLimiter.Pour(uint16(len(lines)))
346
- if !ok {
347
- tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.\n",
348
- tail.Filename)
349
- return false
350
- }
351
- }
352
-
353
- return true
354
-}
355
-
356
-// Cleanup removes inotify watches added by the tail package. This function is
357
-// meant to be invoked from a process's exit handler. Linux kernel may not
358
-// automatically remove inotify watches after the process exits.
359
-func Cleanup() {
360
- watch.Cleanup()
361
-}
Godeps/_workspace/src/github.com/ActiveState/tail/tail_posix.go
deleted
-11
@@ -1,11 +0,0 @@
1
-// +build linux darwin freebsd
2
-
3
-package tail
4
-
5
-import (
6
- "os"
7
-)
8
-
9
-func OpenFile(name string) (file *os.File, err error) {
10
- return os.Open(name)
11
-}
Godeps/_workspace/src/github.com/ActiveState/tail/tail_test.go
deleted
-428
@@ -1,428 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-// TODO:
4
-// * repeat all the tests with Poll:true
5
-
6
-package tail
7
-
8
-import (
9
- "./watch"
10
- _ "fmt"
11
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/ratelimiter"
12
- "io/ioutil"
13
- "os"
14
- "strings"
15
- "testing"
16
- "time"
17
-)
18
-
19
-func init() {
20
- // Clear the temporary test directory
21
- err := os.RemoveAll(".test")
22
- if err != nil {
23
- panic(err)
24
- }
25
-}
26
-
27
-func TestMustExist(t *testing.T) {
28
- tail, err := TailFile("/no/such/file", Config{Follow: true, MustExist: true})
29
- if err == nil {
30
- t.Error("MustExist:true is violated")
31
- tail.Stop()
32
- }
33
- tail, err = TailFile("/no/such/file", Config{Follow: true, MustExist: false})
34
- if err != nil {
35
- t.Error("MustExist:false is violated")
36
- }
37
- tail.Stop()
38
- _, err = TailFile("README.md", Config{Follow: true, MustExist: true})
39
- if err != nil {
40
- t.Error("MustExist:true on an existing file is violated")
41
- }
42
- tail.Stop()
43
- Cleanup()
44
-}
45
-
46
-func TestStop(t *testing.T) {
47
- tail, err := TailFile("_no_such_file", Config{Follow: true, MustExist: false})
48
- if err != nil {
49
- t.Error("MustExist:false is violated")
50
- }
51
- if tail.Stop() != nil {
52
- t.Error("Should be stoped successfully")
53
- }
54
- Cleanup()
55
-}
56
-
57
-func TestMaxLineSize(_t *testing.T) {
58
- t := NewTailTest("maxlinesize", _t)
59
- t.CreateFile("test.txt", "hello\nworld\nfin\nhe")
60
- tail := t.StartTail("test.txt", Config{Follow: true, Location: nil, MaxLineSize: 3})
61
- go t.VerifyTailOutput(tail, []string{"hel", "lo", "wor", "ld", "fin", "he"})
62
-
63
- // Delete after a reasonable delay, to give tail sufficient time
64
- // to read all lines.
65
- <-time.After(100 * time.Millisecond)
66
- t.RemoveFile("test.txt")
67
- tail.Stop()
68
- Cleanup()
69
-}
70
-
71
-func TestOver4096ByteLine(_t *testing.T) {
72
- t := NewTailTest("Over4096ByteLine", _t)
73
- testString := strings.Repeat("a", 4097)
74
- t.CreateFile("test.txt", "test\n"+testString+"\nhello\nworld\n")
75
- tail := t.StartTail("test.txt", Config{Follow: true, Location: nil})
76
- go t.VerifyTailOutput(tail, []string{"test", testString, "hello", "world"})
77
-
78
- // Delete after a reasonable delay, to give tail sufficient time
79
- // to read all lines.
80
- <-time.After(100 * time.Millisecond)
81
- t.RemoveFile("test.txt")
82
- tail.Stop()
83
- Cleanup()
84
-}
85
-func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) {
86
- t := NewTailTest("Over4096ByteLineMaxLineSize", _t)
87
- testString := strings.Repeat("a", 4097)
88
- t.CreateFile("test.txt", "test\n"+testString+"\nhello\nworld\n")
89
- tail := t.StartTail("test.txt", Config{Follow: true, Location: nil, MaxLineSize: 4097})
90
- go t.VerifyTailOutput(tail, []string{"test", testString, "hello", "world"})
91
-
92
- // Delete after a reasonable delay, to give tail sufficient time
93
- // to read all lines.
94
- <-time.After(100 * time.Millisecond)
95
- t.RemoveFile("test.txt")
96
- // tail.Stop()
97
- Cleanup()
98
-}
99
-
100
-func TestLocationFull(_t *testing.T) {
101
- t := NewTailTest("location-full", _t)
102
- t.CreateFile("test.txt", "hello\nworld\n")
103
- tail := t.StartTail("test.txt", Config{Follow: true, Location: nil})
104
- go t.VerifyTailOutput(tail, []string{"hello", "world"})
105
-
106
- // Delete after a reasonable delay, to give tail sufficient time
107
- // to read all lines.
108
- <-time.After(100 * time.Millisecond)
109
- t.RemoveFile("test.txt")
110
- tail.Stop()
111
- Cleanup()
112
-}
113
-
114
-func TestLocationFullDontFollow(_t *testing.T) {
115
- t := NewTailTest("location-full-dontfollow", _t)
116
- t.CreateFile("test.txt", "hello\nworld\n")
117
- tail := t.StartTail("test.txt", Config{Follow: false, Location: nil})
118
- go t.VerifyTailOutput(tail, []string{"hello", "world"})
119
-
120
- // Add more data only after reasonable delay.
121
- <-time.After(100 * time.Millisecond)
122
- t.AppendFile("test.txt", "more\ndata\n")
123
- <-time.After(100 * time.Millisecond)
124
-
125
- tail.Stop()
126
- Cleanup()
127
-}
128
-
129
-func TestLocationEnd(_t *testing.T) {
130
- t := NewTailTest("location-end", _t)
131
- t.CreateFile("test.txt", "hello\nworld\n")
132
- tail := t.StartTail("test.txt", Config{Follow: true, Location: &SeekInfo{0, os.SEEK_END}})
133
- go t.VerifyTailOutput(tail, []string{"more", "data"})
134
-
135
- <-time.After(100 * time.Millisecond)
136
- t.AppendFile("test.txt", "more\ndata\n")
137
-
138
- // Delete after a reasonable delay, to give tail sufficient time
139
- // to read all lines.
140
- <-time.After(100 * time.Millisecond)
141
- t.RemoveFile("test.txt")
142
- tail.Stop()
143
- Cleanup()
144
-}
145
-
146
-func TestLocationMiddle(_t *testing.T) {
147
- // Test reading from middle.
148
- t := NewTailTest("location-end", _t)
149
- t.CreateFile("test.txt", "hello\nworld\n")
150
- tail := t.StartTail("test.txt", Config{Follow: true, Location: &SeekInfo{-6, os.SEEK_END}})
151
- go t.VerifyTailOutput(tail, []string{"world", "more", "data"})
152
-
153
- <-time.After(100 * time.Millisecond)
154
- t.AppendFile("test.txt", "more\ndata\n")
155
-
156
- // Delete after a reasonable delay, to give tail sufficient time
157
- // to read all lines.
158
- <-time.After(100 * time.Millisecond)
159
- t.RemoveFile("test.txt")
160
- tail.Stop()
161
- Cleanup()
162
-}
163
-
164
-func _TestReOpen(_t *testing.T, poll bool) {
165
- var name string
166
- var delay time.Duration
167
- if poll {
168
- name = "reopen-polling"
169
- delay = 300 * time.Millisecond // account for POLL_DURATION
170
- } else {
171
- name = "reopen-inotify"
172
- delay = 100 * time.Millisecond
173
- }
174
- t := NewTailTest(name, _t)
175
- t.CreateFile("test.txt", "hello\nworld\n")
176
- tail := t.StartTail(
177
- "test.txt",
178
- Config{Follow: true, ReOpen: true, Poll: poll})
179
-
180
- go t.VerifyTailOutput(tail, []string{"hello", "world", "more", "data", "endofworld"})
181
-
182
- // deletion must trigger reopen
183
- <-time.After(delay)
184
- t.RemoveFile("test.txt")
185
- <-time.After(delay)
186
- t.CreateFile("test.txt", "more\ndata\n")
187
-
188
- // rename must trigger reopen
189
- <-time.After(delay)
190
- t.RenameFile("test.txt", "test.txt.rotated")
191
- <-time.After(delay)
192
- t.CreateFile("test.txt", "endofworld")
193
-
194
- // Delete after a reasonable delay, to give tail sufficient time
195
- // to read all lines.
196
- <-time.After(delay)
197
- t.RemoveFile("test.txt")
198
- <-time.After(delay)
199
-
200
- // Do not bother with stopping as it could kill the tomb during
201
- // the reading of data written above. Timings can vary based on
202
- // test environment.
203
- // tail.Stop()
204
- Cleanup()
205
-}
206
-
207
-// The use of polling file watcher could affect file rotation
208
-// (detected via renames), so test these explicitly.
209
-
210
-func TestReOpenInotify(_t *testing.T) {
211
- _TestReOpen(_t, false)
212
-}
213
-
214
-func TestReOpenPolling(_t *testing.T) {
215
- _TestReOpen(_t, true)
216
-}
217
-
218
-func _TestReSeek(_t *testing.T, poll bool) {
219
- var name string
220
- if poll {
221
- name = "reseek-polling"
222
- } else {
223
- name = "reseek-inotify"
224
- }
225
- t := NewTailTest(name, _t)
226
- t.CreateFile("test.txt", "a really long string goes here\nhello\nworld\n")
227
- tail := t.StartTail(
228
- "test.txt",
229
- Config{Follow: true, ReOpen: false, Poll: poll})
230
-
231
- go t.VerifyTailOutput(tail, []string{
232
- "a really long string goes here", "hello", "world", "h311o", "w0r1d", "endofworld"})
233
-
234
- // truncate now
235
- <-time.After(100 * time.Millisecond)
236
- t.TruncateFile("test.txt", "h311o\nw0r1d\nendofworld\n")
237
-
238
- // Delete after a reasonable delay, to give tail sufficient time
239
- // to read all lines.
240
- <-time.After(100 * time.Millisecond)
241
- t.RemoveFile("test.txt")
242
-
243
- // Do not bother with stopping as it could kill the tomb during
244
- // the reading of data written above. Timings can vary based on
245
- // test environment.
246
- // tail.Stop()
247
- Cleanup()
248
-}
249
-
250
-// The use of polling file watcher could affect file rotation
251
-// (detected via renames), so test these explicitly.
252
-
253
-func TestReSeekInotify(_t *testing.T) {
254
- _TestReSeek(_t, false)
255
-}
256
-
257
-func TestReSeekPolling(_t *testing.T) {
258
- _TestReSeek(_t, true)
259
-}
260
-
261
-func TestRateLimiting(_t *testing.T) {
262
- t := NewTailTest("rate-limiting", _t)
263
- t.CreateFile("test.txt", "hello\nworld\nagain\nextra\n")
264
- config := Config{
265
- Follow: true,
266
- RateLimiter: ratelimiter.NewLeakyBucket(2, time.Second)}
267
- leakybucketFull := "Too much log activity; waiting a second before resuming tailing"
268
- tail := t.StartTail("test.txt", config)
269
-
270
- // TODO: also verify that tail resumes after the cooloff period.
271
- go t.VerifyTailOutput(tail, []string{
272
- "hello", "world", "again",
273
- leakybucketFull,
274
- "more", "data",
275
- leakybucketFull})
276
-
277
- // Add more data only after reasonable delay.
278
- <-time.After(1200 * time.Millisecond)
279
- t.AppendFile("test.txt", "more\ndata\n")
280
-
281
- // Delete after a reasonable delay, to give tail sufficient time
282
- // to read all lines.
283
- <-time.After(100 * time.Millisecond)
284
- t.RemoveFile("test.txt")
285
-
286
- // tail.Stop()
287
- Cleanup()
288
-}
289
-
290
-func TestTell(_t *testing.T) {
291
- t := NewTailTest("tell-position", _t)
292
- t.CreateFile("test.txt", "hello\nworld\nagain\nmore\n")
293
- config := Config{
294
- Follow: false,
295
- Location: &SeekInfo{0, os.SEEK_SET}}
296
- tail := t.StartTail("test.txt", config)
297
- // read noe line
298
- <-tail.Lines
299
- offset, err := tail.Tell()
300
- if err != nil {
301
- t.Errorf("Tell return error: %s", err.Error())
302
- }
303
- tail.Done()
304
- // tail.close()
305
-
306
- config = Config{
307
- Follow: false,
308
- Location: &SeekInfo{offset, os.SEEK_SET}}
309
- tail = t.StartTail("test.txt", config)
310
- for l := range tail.Lines {
311
- // it may readed one line in the chan(tail.Lines),
312
- // so it may lost one line.
313
- if l.Text != "world" && l.Text != "again" {
314
- t.Fatalf("mismatch; expected world or again, but got %s",
315
- l.Text)
316
- }
317
- break
318
- }
319
- t.RemoveFile("test.txt")
320
- tail.Done()
321
- Cleanup()
322
-}
323
-
324
-// Test library
325
-
326
-type TailTest struct {
327
- Name string
328
- path string
329
- *testing.T
330
-}
331
-
332
-func NewTailTest(name string, t *testing.T) TailTest {
333
- tt := TailTest{name, ".test/" + name, t}
334
- err := os.MkdirAll(tt.path, os.ModeTemporary|0700)
335
- if err != nil {
336
- tt.Fatal(err)
337
- }
338
-
339
- // Use a smaller poll duration for faster test runs. Keep it below
340
- // 100ms (which value is used as common delays for tests)
341
- watch.POLL_DURATION = 5 * time.Millisecond
342
-
343
- return tt
344
-}
345
-
346
-func (t TailTest) CreateFile(name string, contents string) {
347
- err := ioutil.WriteFile(t.path+"/"+name, []byte(contents), 0600)
348
- if err != nil {
349
- t.Fatal(err)
350
- }
351
-}
352
-
353
-func (t TailTest) RemoveFile(name string) {
354
- err := os.Remove(t.path + "/" + name)
355
- if err != nil {
356
- t.Fatal(err)
357
- }
358
-}
359
-
360
-func (t TailTest) RenameFile(oldname string, newname string) {
361
- oldname = t.path + "/" + oldname
362
- newname = t.path + "/" + newname
363
- err := os.Rename(oldname, newname)
364
- if err != nil {
365
- t.Fatal(err)
366
- }
367
-}
368
-
369
-func (t TailTest) AppendFile(name string, contents string) {
370
- f, err := os.OpenFile(t.path+"/"+name, os.O_APPEND|os.O_WRONLY, 0600)
371
- if err != nil {
372
- t.Fatal(err)
373
- }
374
- defer f.Close()
375
- _, err = f.WriteString(contents)
376
- if err != nil {
377
- t.Fatal(err)
378
- }
379
-}
380
-
381
-func (t TailTest) TruncateFile(name string, contents string) {
382
- f, err := os.OpenFile(t.path+"/"+name, os.O_TRUNC|os.O_WRONLY, 0600)
383
- if err != nil {
384
- t.Fatal(err)
385
- }
386
- defer f.Close()
387
- _, err = f.WriteString(contents)
388
- if err != nil {
389
- t.Fatal(err)
390
- }
391
-}
392
-
393
-func (t TailTest) StartTail(name string, config Config) *Tail {
394
- tail, err := TailFile(t.path+"/"+name, config)
395
- if err != nil {
396
- t.Fatal(err)
397
- }
398
- return tail
399
-}
400
-
401
-func (t TailTest) VerifyTailOutput(tail *Tail, lines []string) {
402
- for idx, line := range lines {
403
- tailedLine, ok := <-tail.Lines
404
- if !ok {
405
- // tail.Lines is closed and empty.
406
- err := tail.Err()
407
- if err != nil {
408
- t.Fatalf("tail ended with error: %v", err)
409
- }
410
- t.Fatalf("tail ended early; expecting more: %v", lines[idx:])
411
- }
412
- if tailedLine == nil {
413
- t.Fatalf("tail.Lines returned nil; not possible")
414
- }
415
- // Note: not checking .Err as the `lines` argument is designed
416
- // to match error strings as well.
417
- if tailedLine.Text != line {
418
- t.Fatalf(
419
- "unexpected line/err from tail: "+
420
- "expecting <<%s>>>, but got <<<%s>>>",
421
- line, tailedLine.Text)
422
- }
423
- }
424
- line, ok := <-tail.Lines
425
- if ok {
426
- t.Fatalf("more content from tail: %+v", line)
427
- }
428
-}
Godeps/_workspace/src/github.com/ActiveState/tail/tail_windows.go
deleted
-12
@@ -1,12 +0,0 @@
1
-// +build windows
2
-
3
-package tail
4
-
5
-import (
6
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/winfile"
7
- "os"
8
-)
9
-
10
-func OpenFile(name string) (file *os.File, err error) {
11
- return winfile.OpenFile(name, os.O_RDONLY, 0)
12
-}
Godeps/_workspace/src/github.com/ActiveState/tail/util/util.go
deleted
-47
@@ -1,47 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package util
4
-
5
-import (
6
- "fmt"
7
- "log"
8
- "os"
9
- "runtime/debug"
10
-)
11
-
12
-type Logger struct {
13
- *log.Logger
14
-}
15
-
16
-var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)}
17
-
18
-// fatal is like panic except it displays only the current goroutine's stack.
19
-func Fatal(format string, v ...interface{}) {
20
- // https://github.com/ActiveState/log/blob/master/log.go#L45
21
- LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack()))
22
- os.Exit(1)
23
-}
24
-
25
-// partitionString partitions the string into chunks of given size,
26
-// with the last chunk of variable size.
27
-func PartitionString(s string, chunkSize int) []string {
28
- if chunkSize <= 0 {
29
- panic("invalid chunkSize")
30
- }
31
- length := len(s)
32
- chunks := 1 + length/chunkSize
33
- start := 0
34
- end := chunkSize
35
- parts := make([]string, 0, chunks)
36
- for {
37
- if end > length {
38
- end = length
39
- }
40
- parts = append(parts, s[start:end])
41
- if end == length {
42
- break
43
- }
44
- start, end = end, end+chunkSize
45
- }
46
- return parts
47
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/filechanges.go
deleted
-42
@@ -1,42 +0,0 @@
1
-package watch
2
-
3
-type FileChanges struct {
4
- Modified chan bool // Channel to get notified of modifications
5
- Truncated chan bool // Channel to get notified of truncations
6
- Deleted chan bool // Channel to get notified of deletions/renames
7
-}
8
-
9
-func NewFileChanges() *FileChanges {
10
- return &FileChanges{
11
- make(chan bool), make(chan bool), make(chan bool)}
12
-}
13
-
14
-func (fc *FileChanges) NotifyModified() {
15
- sendOnlyIfEmpty(fc.Modified)
16
-}
17
-
18
-func (fc *FileChanges) NotifyTruncated() {
19
- sendOnlyIfEmpty(fc.Truncated)
20
-}
21
-
22
-func (fc *FileChanges) NotifyDeleted() {
23
- sendOnlyIfEmpty(fc.Deleted)
24
-}
25
-
26
-func (fc *FileChanges) Close() {
27
- close(fc.Modified)
28
- close(fc.Truncated)
29
- close(fc.Deleted)
30
-}
31
-
32
-// sendOnlyIfEmpty sends on a bool channel only if the channel has no
33
-// backlog to be read by other goroutines. This concurrency pattern
34
-// can be used to notify other goroutines if and only if they are
35
-// looking for it (i.e., subsequent notifications can be compressed
36
-// into one).
37
-func sendOnlyIfEmpty(ch chan bool) {
38
- select {
39
- case ch <- true:
40
- default:
41
- }
42
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/inotify.go
deleted
-136
@@ -1,136 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package watch
4
-
5
-import (
6
- "fmt"
7
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/util"
8
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/howeyc/fsnotify"
9
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/tomb.v1"
10
- "os"
11
- "path/filepath"
12
-)
13
-
14
-var inotifyTracker *InotifyTracker
15
-
16
-// InotifyFileWatcher uses inotify to monitor file changes.
17
-type InotifyFileWatcher struct {
18
- Filename string
19
- Size int64
20
-}
21
-
22
-func NewInotifyFileWatcher(filename string) *InotifyFileWatcher {
23
- fw := &InotifyFileWatcher{filename, 0}
24
- return fw
25
-}
26
-
27
-func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
28
- w, err := inotifyTracker.NewWatcher()
29
- if err != nil {
30
- return err
31
- }
32
- defer inotifyTracker.CloseWatcher(w)
33
-
34
- dirname := filepath.Dir(fw.Filename)
35
-
36
- // Watch for new files to be created in the parent directory.
37
- err = w.WatchFlags(dirname, fsnotify.FSN_CREATE)
38
- if err != nil {
39
- return err
40
- }
41
-
42
- // Do a real check now as the file might have been created before
43
- // calling `WatchFlags` above.
44
- if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
45
- // file exists, or stat returned an error.
46
- return err
47
- }
48
-
49
- for {
50
- select {
51
- case evt, ok := <-w.Event:
52
- if !ok {
53
- return fmt.Errorf("inotify watcher has been closed")
54
- } else if evt.Name == fw.Filename {
55
- return nil
56
- }
57
- case <-t.Dying():
58
- return tomb.ErrDying
59
- }
60
- }
61
- panic("unreachable")
62
-}
63
-
64
-func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges {
65
- changes := NewFileChanges()
66
-
67
- w, err := inotifyTracker.NewWatcher()
68
- if err != nil {
69
- util.Fatal("Error creating fsnotify watcher: %v", err)
70
- }
71
- err = w.Watch(fw.Filename)
72
- if err != nil {
73
- util.Fatal("Error watching %v: %v", fw.Filename, err)
74
- }
75
-
76
- fw.Size = fi.Size()
77
-
78
- go func() {
79
- defer inotifyTracker.CloseWatcher(w)
80
- defer changes.Close()
81
-
82
- for {
83
- prevSize := fw.Size
84
-
85
- var evt *fsnotify.FileEvent
86
- var ok bool
87
-
88
- select {
89
- case evt, ok = <-w.Event:
90
- if !ok {
91
- return
92
- }
93
- case <-t.Dying():
94
- return
95
- }
96
-
97
- switch {
98
- case evt.IsDelete():
99
- fallthrough
100
-
101
- case evt.IsRename():
102
- changes.NotifyDeleted()
103
- return
104
-
105
- case evt.IsModify():
106
- fi, err := os.Stat(fw.Filename)
107
- if err != nil {
108
- if os.IsNotExist(err) {
109
- changes.NotifyDeleted()
110
- return
111
- }
112
- // XXX: report this error back to the user
113
- util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
114
- }
115
- fw.Size = fi.Size()
116
-
117
- if prevSize > 0 && prevSize > fw.Size {
118
- changes.NotifyTruncated()
119
- } else {
120
- changes.NotifyModified()
121
- }
122
- prevSize = fw.Size
123
- }
124
- }
125
- }()
126
-
127
- return changes
128
-}
129
-
130
-func Cleanup() {
131
- inotifyTracker.CloseAll()
132
-}
133
-
134
-func init() {
135
- inotifyTracker = NewInotifyTracker()
136
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/inotify_tracker.go
deleted
-51
@@ -1,51 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package watch
4
-
5
-import (
6
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/howeyc/fsnotify"
7
- "log"
8
- "sync"
9
-)
10
-
11
-type InotifyTracker struct {
12
- mux sync.Mutex
13
- watchers map[*fsnotify.Watcher]bool
14
-}
15
-
16
-func NewInotifyTracker() *InotifyTracker {
17
- t := new(InotifyTracker)
18
- t.watchers = make(map[*fsnotify.Watcher]bool)
19
- return t
20
-}
21
-
22
-func (t *InotifyTracker) NewWatcher() (*fsnotify.Watcher, error) {
23
- t.mux.Lock()
24
- defer t.mux.Unlock()
25
- w, err := fsnotify.NewWatcher()
26
- if err == nil {
27
- t.watchers[w] = true
28
- }
29
- return w, err
30
-}
31
-
32
-func (t *InotifyTracker) CloseWatcher(w *fsnotify.Watcher) (err error) {
33
- t.mux.Lock()
34
- defer t.mux.Unlock()
35
- if _, ok := t.watchers[w]; ok {
36
- err = w.Close()
37
- delete(t.watchers, w)
38
- }
39
- return
40
-}
41
-
42
-func (t *InotifyTracker) CloseAll() {
43
- t.mux.Lock()
44
- defer t.mux.Unlock()
45
- for w, _ := range t.watchers {
46
- if err := w.Close(); err != nil {
47
- log.Printf("Error closing watcher: %v", err)
48
- }
49
- delete(t.watchers, w)
50
- }
51
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/polling.go
deleted
-110
@@ -1,110 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package watch
4
-
5
-import (
6
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ActiveState/tail/util"
7
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/tomb.v1"
8
- "os"
9
- "time"
10
-)
11
-
12
-// PollingFileWatcher polls the file for changes.
13
-type PollingFileWatcher struct {
14
- Filename string
15
- Size int64
16
-}
17
-
18
-func NewPollingFileWatcher(filename string) *PollingFileWatcher {
19
- fw := &PollingFileWatcher{filename, 0}
20
- return fw
21
-}
22
-
23
-var POLL_DURATION time.Duration
24
-
25
-func (fw *PollingFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
26
- for {
27
- if _, err := os.Stat(fw.Filename); err == nil {
28
- return nil
29
- } else if !os.IsNotExist(err) {
30
- return err
31
- }
32
- select {
33
- case <-time.After(POLL_DURATION):
34
- continue
35
- case <-t.Dying():
36
- return tomb.ErrDying
37
- }
38
- }
39
- panic("unreachable")
40
-}
41
-
42
-func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *FileChanges {
43
- changes := NewFileChanges()
44
- var prevModTime time.Time
45
-
46
- // XXX: use tomb.Tomb to cleanly manage these goroutines. replace
47
- // the fatal (below) with tomb's Kill.
48
-
49
- fw.Size = origFi.Size()
50
-
51
- go func() {
52
- defer changes.Close()
53
-
54
- var retry int = 0
55
-
56
- prevSize := fw.Size
57
- for {
58
- select {
59
- case <-t.Dying():
60
- return
61
- default:
62
- }
63
-
64
- time.Sleep(POLL_DURATION)
65
- fi, err := os.Stat(fw.Filename)
66
- if err != nil {
67
- if os.IsNotExist(err) {
68
- // File does not exist (has been deleted).
69
- changes.NotifyDeleted()
70
- return
71
- }
72
-
73
- if permissionErrorRetry(err, &retry) {
74
- continue
75
- }
76
-
77
- // XXX: report this error back to the user
78
- util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
79
- }
80
-
81
- // File got moved/renamed?
82
- if !os.SameFile(origFi, fi) {
83
- changes.NotifyDeleted()
84
- return
85
- }
86
-
87
- // File got truncated?
88
- fw.Size = fi.Size()
89
- if prevSize > 0 && prevSize > fw.Size {
90
- changes.NotifyTruncated()
91
- prevSize = fw.Size
92
- continue
93
- }
94
- prevSize = fw.Size
95
-
96
- // File was appended to (changed)?
97
- modTime := fi.ModTime()
98
- if modTime != prevModTime {
99
- prevModTime = modTime
100
- changes.NotifyModified()
101
- }
102
- }
103
- }()
104
-
105
- return changes
106
-}
107
-
108
-func init() {
109
- POLL_DURATION = 250 * time.Millisecond
110
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/polling_posix.go
deleted
-9
@@ -1,9 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-// +build linux darwin freebsd
3
-
4
-package watch
5
-
6
-func permissionErrorRetry(err error, retry *int) bool {
7
- // No need for this on linux, don't retry
8
- return false
9
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/polling_windows.go
deleted
-18
@@ -1,18 +0,0 @@
1
-// +build windows
2
-
3
-package watch
4
-
5
-import (
6
- "os"
7
-)
8
-
9
-const permissionDeniedRetryCount int = 5
10
-
11
-func permissionErrorRetry(err error, retry *int) bool {
12
- if os.IsPermission(err) && *retry < permissionDeniedRetryCount {
13
- // While pooling a file that does not exist yet, but will be created by another process we can get Permission Denied
14
- (*retry)++
15
- return true
16
- }
17
- return false
18
-}
Godeps/_workspace/src/github.com/ActiveState/tail/watch/watch.go
deleted
-20
@@ -1,20 +0,0 @@
1
-// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
2
-
3
-package watch
4
-
5
-import (
6
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/tomb.v1"
7
- "os"
8
-)
9
-
10
-// FileWatcher monitors file-level events.
11
-type FileWatcher interface {
12
- // BlockUntilExists blocks until the file comes into existence.
13
- BlockUntilExists(*tomb.Tomb) error
14
-
15
- // ChangeEvents reports on changes to a file, be it modification,
16
- // deletion, renames or truncations. Returned FileChanges group of
17
- // channels will be closed, thus become unusable, after a deletion
18
- // or truncation event.
19
- ChangeEvents(*tomb.Tomb, os.FileInfo) *FileChanges
20
-}
Godeps/_workspace/src/github.com/ActiveState/tail/winfile/winfile.go
deleted
-92
@@ -1,92 +0,0 @@
1
-// +build windows
2
-
3
-package winfile
4
-
5
-import (
6
- "os"
7
- "syscall"
8
- "unsafe"
9
-)
10
-
11
-// issue also described here
12
-//https://codereview.appspot.com/8203043/
13
-
14
-// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L218
15
-func Open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
16
- if len(path) == 0 {
17
- return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
18
- }
19
- pathp, err := syscall.UTF16PtrFromString(path)
20
- if err != nil {
21
- return syscall.InvalidHandle, err
22
- }
23
- var access uint32
24
- switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
25
- case syscall.O_RDONLY:
26
- access = syscall.GENERIC_READ
27
- case syscall.O_WRONLY:
28
- access = syscall.GENERIC_WRITE
29
- case syscall.O_RDWR:
30
- access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
31
- }
32
- if mode&syscall.O_CREAT != 0 {
33
- access |= syscall.GENERIC_WRITE
34
- }
35
- if mode&syscall.O_APPEND != 0 {
36
- access &^= syscall.GENERIC_WRITE
37
- access |= syscall.FILE_APPEND_DATA
38
- }
39
- sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
40
- var sa *syscall.SecurityAttributes
41
- if mode&syscall.O_CLOEXEC == 0 {
42
- sa = makeInheritSa()
43
- }
44
- var createmode uint32
45
- switch {
46
- case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
47
- createmode = syscall.CREATE_NEW
48
- case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
49
- createmode = syscall.CREATE_ALWAYS
50
- case mode&syscall.O_CREAT == syscall.O_CREAT:
51
- createmode = syscall.OPEN_ALWAYS
52
- case mode&syscall.O_TRUNC == syscall.O_TRUNC:
53
- createmode = syscall.TRUNCATE_EXISTING
54
- default:
55
- createmode = syscall.OPEN_EXISTING
56
- }
57
- h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
58
- return h, e
59
-}
60
-
61
-// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L211
62
-func makeInheritSa() *syscall.SecurityAttributes {
63
- var sa syscall.SecurityAttributes
64
- sa.Length = uint32(unsafe.Sizeof(sa))
65
- sa.InheritHandle = 1
66
- return &sa
67
-}
68
-
69
-// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_windows.go#L133
70
-func OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
71
- r, e := Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
72
- if e != nil {
73
- return nil, e
74
- }
75
- return os.NewFile(uintptr(r), name), nil
76
-}
77
-
78
-// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_posix.go#L61
79
-func syscallMode(i os.FileMode) (o uint32) {
80
- o |= uint32(i.Perm())
81
- if i&os.ModeSetuid != 0 {
82
- o |= syscall.S_ISUID
83
- }
84
- if i&os.ModeSetgid != 0 {
85
- o |= syscall.S_ISGID
86
- }
87
- if i&os.ModeSticky != 0 {
88
- o |= syscall.S_ISVTX
89
- }
90
- // No mapping for Go's ModeTemporary (plan9 only).
91
- return
92
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/.gitignore
deleted
-5
@@ -1,5 +0,0 @@
1
-# Setup a Global .gitignore for OS and editor generated files:
2
-# https://help.github.com/articles/ignoring-files
3
-# git config --global core.excludesfile ~/.gitignore_global
4
-
5
-.vagrant
Godeps/_workspace/src/github.com/howeyc/fsnotify/AUTHORS
deleted
-28
@@ -1,28 +0,0 @@
1
-# Names should be added to this file as
2
-# Name or Organization <email address>
3
-# The email address is not required for organizations.
4
-
5
-# You can update this list using the following command:
6
-#
7
-# $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
8
-
9
-# Please keep the list sorted.
10
-
11
-Adrien Bustany <adrien@bustany.org>
12
-Caleb Spare <cespare@gmail.com>
13
-Case Nelson <case@teammating.com>
14
-Chris Howey <howeyc@gmail.com> <chris@howey.me>
15
-Christoffer Buchholz <christoffer.buchholz@gmail.com>
16
-Dave Cheney <dave@cheney.net>
17
-Francisco Souza <f@souza.cc>
18
-John C Barstow
19
-Kelvin Fo <vmirage@gmail.com>
20
-Nathan Youngman <git@nathany.com>
21
-Paul Hammond <paul@paulhammond.org>
22
-Pursuit92 <JoshChase@techpursuit.net>
23
-Rob Figueiredo <robfig@gmail.com>
24
-Travis Cline <travis.cline@gmail.com>
25
-Tudor Golubenco <tudor.g@gmail.com>
26
-bronze1man <bronze1man@gmail.com>
27
-debrando <denis.brandolini@gmail.com>
28
-henrikedwards <henrik.edwards@gmail.com>
Godeps/_workspace/src/github.com/howeyc/fsnotify/CHANGELOG.md
deleted
-160
@@ -1,160 +0,0 @@
1
-# Changelog
2
-
3
-## v0.9.0 / 2014-01-17
4
-
5
-* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
6
-* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
7
-* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
8
-
9
-## v0.8.12 / 2013-11-13
10
-
11
-* [API] Remove FD_SET and friends from Linux adapter
12
-
13
-## v0.8.11 / 2013-11-02
14
-
15
-* [Doc] Add Changelog [#72][] (thanks @nathany)
16
-* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond)
17
-
18
-## v0.8.10 / 2013-10-19
19
-
20
-* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
21
-* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
22
-* [Doc] specify OS-specific limits in README (thanks @debrando)
23
-
24
-## v0.8.9 / 2013-09-08
25
-
26
-* [Doc] Contributing (thanks @nathany)
27
-* [Doc] update package path in example code [#63][] (thanks @paulhammond)
28
-* [Doc] GoCI badge in README (Linux only) [#60][]
29
-* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
30
-
31
-## v0.8.8 / 2013-06-17
32
-
33
-* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
34
-
35
-## v0.8.7 / 2013-06-03
36
-
37
-* [API] Make syscall flags internal
38
-* [Fix] inotify: ignore event changes
39
-* [Fix] race in symlink test [#45][] (reported by @srid)
40
-* [Fix] tests on Windows
41
-* lower case error messages
42
-
43
-## v0.8.6 / 2013-05-23
44
-
45
-* kqueue: Use EVT_ONLY flag on Darwin
46
-* [Doc] Update README with full example
47
-
48
-## v0.8.5 / 2013-05-09
49
-
50
-* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
51
-
52
-## v0.8.4 / 2013-04-07
53
-
54
-* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
55
-
56
-## v0.8.3 / 2013-03-13
57
-
58
-* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
59
-* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
60
-
61
-## v0.8.2 / 2013-02-07
62
-
63
-* [Doc] add Authors
64
-* [Fix] fix data races for map access [#29][] (thanks @fsouza)
65
-
66
-## v0.8.1 / 2013-01-09
67
-
68
-* [Fix] Windows path separators
69
-* [Doc] BSD License
70
-
71
-## v0.8.0 / 2012-11-09
72
-
73
-* kqueue: directory watching improvements (thanks @vmirage)
74
-* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
75
-* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
76
-
77
-## v0.7.4 / 2012-10-09
78
-
79
-* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
80
-* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
81
-* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
82
-* [Fix] kqueue: modify after recreation of file
83
-
84
-## v0.7.3 / 2012-09-27
85
-
86
-* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
87
-* [Fix] kqueue: no longer get duplicate CREATE events
88
-
89
-## v0.7.2 / 2012-09-01
90
-
91
-* kqueue: events for created directories
92
-
93
-## v0.7.1 / 2012-07-14
94
-
95
-* [Fix] for renaming files
96
-
97
-## v0.7.0 / 2012-07-02
98
-
99
-* [Feature] FSNotify flags
100
-* [Fix] inotify: Added file name back to event path
101
-
102
-## v0.6.0 / 2012-06-06
103
-
104
-* kqueue: watch files after directory created (thanks @tmc)
105
-
106
-## v0.5.1 / 2012-05-22
107
-
108
-* [Fix] inotify: remove all watches before Close()
109
-
110
-## v0.5.0 / 2012-05-03
111
-
112
-* [API] kqueue: return errors during watch instead of sending over channel
113
-* kqueue: match symlink behavior on Linux
114
-* inotify: add `DELETE_SELF` (requested by @taralx)
115
-* [Fix] kqueue: handle EINTR (reported by @robfig)
116
-* [Doc] Godoc example [#1][] (thanks @davecheney)
117
-
118
-## v0.4.0 / 2012-03-30
119
-
120
-* Go 1 released: build with go tool
121
-* [Feature] Windows support using winfsnotify
122
-* Windows does not have attribute change notifications
123
-* Roll attribute notifications into IsModify
124
-
125
-## v0.3.0 / 2012-02-19
126
-
127
-* kqueue: add files when watch directory
128
-
129
-## v0.2.0 / 2011-12-30
130
-
131
-* update to latest Go weekly code
132
-
133
-## v0.1.0 / 2011-10-19
134
-
135
-* kqueue: add watch on file creation to match inotify
136
-* kqueue: create file event
137
-* inotify: ignore `IN_IGNORED` events
138
-* event String()
139
-* linux: common FileEvent functions
140
-* initial commit
141
-
142
-[#79]: https://github.com/howeyc/fsnotify/pull/79
143
-[#77]: https://github.com/howeyc/fsnotify/pull/77
144
-[#72]: https://github.com/howeyc/fsnotify/issues/72
145
-[#71]: https://github.com/howeyc/fsnotify/issues/71
146
-[#70]: https://github.com/howeyc/fsnotify/issues/70
147
-[#63]: https://github.com/howeyc/fsnotify/issues/63
148
-[#62]: https://github.com/howeyc/fsnotify/issues/62
149
-[#60]: https://github.com/howeyc/fsnotify/issues/60
150
-[#59]: https://github.com/howeyc/fsnotify/issues/59
151
-[#49]: https://github.com/howeyc/fsnotify/issues/49
152
-[#45]: https://github.com/howeyc/fsnotify/issues/45
153
-[#40]: https://github.com/howeyc/fsnotify/issues/40
154
-[#36]: https://github.com/howeyc/fsnotify/issues/36
155
-[#33]: https://github.com/howeyc/fsnotify/issues/33
156
-[#29]: https://github.com/howeyc/fsnotify/issues/29
157
-[#25]: https://github.com/howeyc/fsnotify/issues/25
158
-[#24]: https://github.com/howeyc/fsnotify/issues/24
159
-[#21]: https://github.com/howeyc/fsnotify/issues/21
160
-[#1]: https://github.com/howeyc/fsnotify/issues/1
Godeps/_workspace/src/github.com/howeyc/fsnotify/CONTRIBUTING.md
deleted
-7
@@ -1,7 +0,0 @@
1
-# Contributing
2
-
3
-## Moving Notice
4
-
5
-There is a fork being actively developed with a new API in preparation for the Go Standard Library:
6
-[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
7
-
Godeps/_workspace/src/github.com/howeyc/fsnotify/LICENSE
deleted
-28
@@ -1,28 +0,0 @@
1
-Copyright (c) 2012 The Go Authors. All rights reserved.
2
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
3
-
4
-Redistribution and use in source and binary forms, with or without
5
-modification, are permitted provided that the following conditions are
6
-met:
7
-
8
- * Redistributions of source code must retain the above copyright
9
-notice, this list of conditions and the following disclaimer.
10
- * Redistributions in binary form must reproduce the above
11
-copyright notice, this list of conditions and the following disclaimer
12
-in the documentation and/or other materials provided with the
13
-distribution.
14
- * Neither the name of Google Inc. nor the names of its
15
-contributors may be used to endorse or promote products derived from
16
-this software without specific prior written permission.
17
-
18
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/howeyc/fsnotify/README.md
deleted
-92
@@ -1,92 +0,0 @@
1
-# File system notifications for Go
2
-
3
-[](http://godoc.org/github.com/howeyc/fsnotify)
4
-
5
-Cross platform: Windows, Linux, BSD and OS X.
6
-
7
-## Moving Notice
8
-
9
-There is a fork being actively developed with a new API in preparation for the Go Standard Library:
10
-[github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify)
11
-
12
-## Example:
13
-
14
-```go
15
-package main
16
-
17
-import (
18
- "log"
19
-
20
- "github.com/howeyc/fsnotify"
21
-)
22
-
23
-func main() {
24
- watcher, err := fsnotify.NewWatcher()
25
- if err != nil {
26
- log.Fatal(err)
27
- }
28
-
29
- done := make(chan bool)
30
-
31
- // Process events
32
- go func() {
33
- for {
34
- select {
35
- case ev := <-watcher.Event:
36
- log.Println("event:", ev)
37
- case err := <-watcher.Error:
38
- log.Println("error:", err)
39
- }
40
- }
41
- }()
42
-
43
- err = watcher.Watch("testDir")
44
- if err != nil {
45
- log.Fatal(err)
46
- }
47
-
48
- <-done
49
-
50
- /* ... do stuff ... */
51
- watcher.Close()
52
-}
53
-```
54
-
55
-For each event:
56
-* Name
57
-* IsCreate()
58
-* IsDelete()
59
-* IsModify()
60
-* IsRename()
61
-
62
-## FAQ
63
-
64
-**When a file is moved to another directory is it still being watched?**
65
-
66
-No (it shouldn't be, unless you are watching where it was moved to).
67
-
68
-**When I watch a directory, are all subdirectories watched as well?**
69
-
70
-No, you must add watches for any directory you want to watch (a recursive watcher is in the works [#56][]).
71
-
72
-**Do I have to watch the Error and Event channels in a separate goroutine?**
73
-
74
-As of now, yes. Looking into making this single-thread friendly (see [#7][])
75
-
76
-**Why am I receiving multiple events for the same file on OS X?**
77
-
78
-Spotlight indexing on OS X can result in multiple events (see [#62][]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#54][]).
79
-
80
-**How many files can be watched at once?**
81
-
82
-There are OS-specific limits as to how many watches can be created:
83
-* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit,
84
-reaching this limit results in a "no space left on device" error.
85
-* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
86
-
87
-
88
-[#62]: https://github.com/howeyc/fsnotify/issues/62
89
-[#56]: https://github.com/howeyc/fsnotify/issues/56
90
-[#54]: https://github.com/howeyc/fsnotify/issues/54
91
-[#7]: https://github.com/howeyc/fsnotify/issues/7
92
-
Godeps/_workspace/src/github.com/howeyc/fsnotify/example_test.go
deleted
-34
@@ -1,34 +0,0 @@
1
-// Copyright 2012 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package fsnotify_test
6
-
7
-import (
8
- "log"
9
-
10
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/howeyc/fsnotify"
11
-)
12
-
13
-func ExampleNewWatcher() {
14
- watcher, err := fsnotify.NewWatcher()
15
- if err != nil {
16
- log.Fatal(err)
17
- }
18
-
19
- go func() {
20
- for {
21
- select {
22
- case ev := <-watcher.Event:
23
- log.Println("event:", ev)
24
- case err := <-watcher.Error:
25
- log.Println("error:", err)
26
- }
27
- }
28
- }()
29
-
30
- err = watcher.Watch("/tmp/foo")
31
- if err != nil {
32
- log.Fatal(err)
33
- }
34
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify.go
deleted
-111
@@ -1,111 +0,0 @@
1
-// Copyright 2012 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// Package fsnotify implements file system notification.
6
-package fsnotify
7
-
8
-import "fmt"
9
-
10
-const (
11
- FSN_CREATE = 1
12
- FSN_MODIFY = 2
13
- FSN_DELETE = 4
14
- FSN_RENAME = 8
15
-
16
- FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE
17
-)
18
-
19
-// Purge events from interal chan to external chan if passes filter
20
-func (w *Watcher) purgeEvents() {
21
- for ev := range w.internalEvent {
22
- sendEvent := false
23
- w.fsnmut.Lock()
24
- fsnFlags := w.fsnFlags[ev.Name]
25
- w.fsnmut.Unlock()
26
-
27
- if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() {
28
- sendEvent = true
29
- }
30
-
31
- if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() {
32
- sendEvent = true
33
- }
34
-
35
- if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() {
36
- sendEvent = true
37
- }
38
-
39
- if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() {
40
- sendEvent = true
41
- }
42
-
43
- if sendEvent {
44
- w.Event <- ev
45
- }
46
-
47
- // If there's no file, then no more events for user
48
- // BSD must keep watch for internal use (watches DELETEs to keep track
49
- // what files exist for create events)
50
- if ev.IsDelete() {
51
- w.fsnmut.Lock()
52
- delete(w.fsnFlags, ev.Name)
53
- w.fsnmut.Unlock()
54
- }
55
- }
56
-
57
- close(w.Event)
58
-}
59
-
60
-// Watch a given file path
61
-func (w *Watcher) Watch(path string) error {
62
- return w.WatchFlags(path, FSN_ALL)
63
-}
64
-
65
-// Watch a given file path for a particular set of notifications (FSN_MODIFY etc.)
66
-func (w *Watcher) WatchFlags(path string, flags uint32) error {
67
- w.fsnmut.Lock()
68
- w.fsnFlags[path] = flags
69
- w.fsnmut.Unlock()
70
- return w.watch(path)
71
-}
72
-
73
-// Remove a watch on a file
74
-func (w *Watcher) RemoveWatch(path string) error {
75
- w.fsnmut.Lock()
76
- delete(w.fsnFlags, path)
77
- w.fsnmut.Unlock()
78
- return w.removeWatch(path)
79
-}
80
-
81
-// String formats the event e in the form
82
-// "filename: DELETE|MODIFY|..."
83
-func (e *FileEvent) String() string {
84
- var events string = ""
85
-
86
- if e.IsCreate() {
87
- events += "|" + "CREATE"
88
- }
89
-
90
- if e.IsDelete() {
91
- events += "|" + "DELETE"
92
- }
93
-
94
- if e.IsModify() {
95
- events += "|" + "MODIFY"
96
- }
97
-
98
- if e.IsRename() {
99
- events += "|" + "RENAME"
100
- }
101
-
102
- if e.IsAttrib() {
103
- events += "|" + "ATTRIB"
104
- }
105
-
106
- if len(events) > 0 {
107
- events = events[1:]
108
- }
109
-
110
- return fmt.Sprintf("%q: %s", e.Name, events)
111
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_bsd.go
deleted
-496
@@ -1,496 +0,0 @@
1
-// Copyright 2010 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build freebsd openbsd netbsd darwin
6
-
7
-package fsnotify
8
-
9
-import (
10
- "errors"
11
- "fmt"
12
- "io/ioutil"
13
- "os"
14
- "path/filepath"
15
- "sync"
16
- "syscall"
17
-)
18
-
19
-const (
20
- // Flags (from <sys/event.h>)
21
- sys_NOTE_DELETE = 0x0001 /* vnode was removed */
22
- sys_NOTE_WRITE = 0x0002 /* data contents changed */
23
- sys_NOTE_EXTEND = 0x0004 /* size increased */
24
- sys_NOTE_ATTRIB = 0x0008 /* attributes changed */
25
- sys_NOTE_LINK = 0x0010 /* link count changed */
26
- sys_NOTE_RENAME = 0x0020 /* vnode was renamed */
27
- sys_NOTE_REVOKE = 0x0040 /* vnode access was revoked */
28
-
29
- // Watch all events
30
- sys_NOTE_ALLEVENTS = sys_NOTE_DELETE | sys_NOTE_WRITE | sys_NOTE_ATTRIB | sys_NOTE_RENAME
31
-
32
- // Block for 100 ms on each call to kevent
33
- keventWaitTime = 100e6
34
-)
35
-
36
-type FileEvent struct {
37
- mask uint32 // Mask of events
38
- Name string // File name (optional)
39
- create bool // set by fsnotify package if found new file
40
-}
41
-
42
-// IsCreate reports whether the FileEvent was triggered by a creation
43
-func (e *FileEvent) IsCreate() bool { return e.create }
44
-
45
-// IsDelete reports whether the FileEvent was triggered by a delete
46
-func (e *FileEvent) IsDelete() bool { return (e.mask & sys_NOTE_DELETE) == sys_NOTE_DELETE }
47
-
48
-// IsModify reports whether the FileEvent was triggered by a file modification
49
-func (e *FileEvent) IsModify() bool {
50
- return ((e.mask&sys_NOTE_WRITE) == sys_NOTE_WRITE || (e.mask&sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB)
51
-}
52
-
53
-// IsRename reports whether the FileEvent was triggered by a change name
54
-func (e *FileEvent) IsRename() bool { return (e.mask & sys_NOTE_RENAME) == sys_NOTE_RENAME }
55
-
56
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
57
-func (e *FileEvent) IsAttrib() bool {
58
- return (e.mask & sys_NOTE_ATTRIB) == sys_NOTE_ATTRIB
59
-}
60
-
61
-type Watcher struct {
62
- mu sync.Mutex // Mutex for the Watcher itself.
63
- kq int // File descriptor (as returned by the kqueue() syscall)
64
- watches map[string]int // Map of watched file descriptors (key: path)
65
- wmut sync.Mutex // Protects access to watches.
66
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
67
- fsnmut sync.Mutex // Protects access to fsnFlags.
68
- enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue
69
- enmut sync.Mutex // Protects access to enFlags.
70
- paths map[int]string // Map of watched paths (key: watch descriptor)
71
- finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
72
- pmut sync.Mutex // Protects access to paths and finfo.
73
- fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events)
74
- femut sync.Mutex // Protects access to fileExists.
75
- externalWatches map[string]bool // Map of watches added by user of the library.
76
- ewmut sync.Mutex // Protects access to externalWatches.
77
- Error chan error // Errors are sent on this channel
78
- internalEvent chan *FileEvent // Events are queued on this channel
79
- Event chan *FileEvent // Events are returned on this channel
80
- done chan bool // Channel for sending a "quit message" to the reader goroutine
81
- isClosed bool // Set to true when Close() is first called
82
-}
83
-
84
-// NewWatcher creates and returns a new kevent instance using kqueue(2)
85
-func NewWatcher() (*Watcher, error) {
86
- fd, errno := syscall.Kqueue()
87
- if fd == -1 {
88
- return nil, os.NewSyscallError("kqueue", errno)
89
- }
90
- w := &Watcher{
91
- kq: fd,
92
- watches: make(map[string]int),
93
- fsnFlags: make(map[string]uint32),
94
- enFlags: make(map[string]uint32),
95
- paths: make(map[int]string),
96
- finfo: make(map[int]os.FileInfo),
97
- fileExists: make(map[string]bool),
98
- externalWatches: make(map[string]bool),
99
- internalEvent: make(chan *FileEvent),
100
- Event: make(chan *FileEvent),
101
- Error: make(chan error),
102
- done: make(chan bool, 1),
103
- }
104
-
105
- go w.readEvents()
106
- go w.purgeEvents()
107
- return w, nil
108
-}
109
-
110
-// Close closes a kevent watcher instance
111
-// It sends a message to the reader goroutine to quit and removes all watches
112
-// associated with the kevent instance
113
-func (w *Watcher) Close() error {
114
- w.mu.Lock()
115
- if w.isClosed {
116
- w.mu.Unlock()
117
- return nil
118
- }
119
- w.isClosed = true
120
- w.mu.Unlock()
121
-
122
- // Send "quit" message to the reader goroutine
123
- w.done <- true
124
- w.wmut.Lock()
125
- ws := w.watches
126
- w.wmut.Unlock()
127
- for path := range ws {
128
- w.removeWatch(path)
129
- }
130
-
131
- return nil
132
-}
133
-
134
-// AddWatch adds path to the watched file set.
135
-// The flags are interpreted as described in kevent(2).
136
-func (w *Watcher) addWatch(path string, flags uint32) error {
137
- w.mu.Lock()
138
- if w.isClosed {
139
- w.mu.Unlock()
140
- return errors.New("kevent instance already closed")
141
- }
142
- w.mu.Unlock()
143
-
144
- watchDir := false
145
-
146
- w.wmut.Lock()
147
- watchfd, found := w.watches[path]
148
- w.wmut.Unlock()
149
- if !found {
150
- fi, errstat := os.Lstat(path)
151
- if errstat != nil {
152
- return errstat
153
- }
154
-
155
- // don't watch socket
156
- if fi.Mode()&os.ModeSocket == os.ModeSocket {
157
- return nil
158
- }
159
-
160
- // Follow Symlinks
161
- // Unfortunately, Linux can add bogus symlinks to watch list without
162
- // issue, and Windows can't do symlinks period (AFAIK). To maintain
163
- // consistency, we will act like everything is fine. There will simply
164
- // be no file events for broken symlinks.
165
- // Hence the returns of nil on errors.
166
- if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
167
- path, err := filepath.EvalSymlinks(path)
168
- if err != nil {
169
- return nil
170
- }
171
-
172
- fi, errstat = os.Lstat(path)
173
- if errstat != nil {
174
- return nil
175
- }
176
- }
177
-
178
- fd, errno := syscall.Open(path, open_FLAGS, 0700)
179
- if fd == -1 {
180
- return errno
181
- }
182
- watchfd = fd
183
-
184
- w.wmut.Lock()
185
- w.watches[path] = watchfd
186
- w.wmut.Unlock()
187
-
188
- w.pmut.Lock()
189
- w.paths[watchfd] = path
190
- w.finfo[watchfd] = fi
191
- w.pmut.Unlock()
192
- }
193
- // Watch the directory if it has not been watched before.
194
- w.pmut.Lock()
195
- w.enmut.Lock()
196
- if w.finfo[watchfd].IsDir() &&
197
- (flags&sys_NOTE_WRITE) == sys_NOTE_WRITE &&
198
- (!found || (w.enFlags[path]&sys_NOTE_WRITE) != sys_NOTE_WRITE) {
199
- watchDir = true
200
- }
201
- w.enmut.Unlock()
202
- w.pmut.Unlock()
203
-
204
- w.enmut.Lock()
205
- w.enFlags[path] = flags
206
- w.enmut.Unlock()
207
-
208
- var kbuf [1]syscall.Kevent_t
209
- watchEntry := &kbuf[0]
210
- watchEntry.Fflags = flags
211
- syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)
212
- entryFlags := watchEntry.Flags
213
- success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
214
- if success == -1 {
215
- return errno
216
- } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
217
- return errors.New("kevent add error")
218
- }
219
-
220
- if watchDir {
221
- errdir := w.watchDirectoryFiles(path)
222
- if errdir != nil {
223
- return errdir
224
- }
225
- }
226
- return nil
227
-}
228
-
229
-// Watch adds path to the watched file set, watching all events.
230
-func (w *Watcher) watch(path string) error {
231
- w.ewmut.Lock()
232
- w.externalWatches[path] = true
233
- w.ewmut.Unlock()
234
- return w.addWatch(path, sys_NOTE_ALLEVENTS)
235
-}
236
-
237
-// RemoveWatch removes path from the watched file set.
238
-func (w *Watcher) removeWatch(path string) error {
239
- w.wmut.Lock()
240
- watchfd, ok := w.watches[path]
241
- w.wmut.Unlock()
242
- if !ok {
243
- return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
244
- }
245
- var kbuf [1]syscall.Kevent_t
246
- watchEntry := &kbuf[0]
247
- syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
248
- entryFlags := watchEntry.Flags
249
- success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
250
- if success == -1 {
251
- return os.NewSyscallError("kevent_rm_watch", errno)
252
- } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
253
- return errors.New("kevent rm error")
254
- }
255
- syscall.Close(watchfd)
256
- w.wmut.Lock()
257
- delete(w.watches, path)
258
- w.wmut.Unlock()
259
- w.enmut.Lock()
260
- delete(w.enFlags, path)
261
- w.enmut.Unlock()
262
- w.pmut.Lock()
263
- delete(w.paths, watchfd)
264
- fInfo := w.finfo[watchfd]
265
- delete(w.finfo, watchfd)
266
- w.pmut.Unlock()
267
-
268
- // Find all watched paths that are in this directory that are not external.
269
- if fInfo.IsDir() {
270
- var pathsToRemove []string
271
- w.pmut.Lock()
272
- for _, wpath := range w.paths {
273
- wdir, _ := filepath.Split(wpath)
274
- if filepath.Clean(wdir) == filepath.Clean(path) {
275
- w.ewmut.Lock()
276
- if !w.externalWatches[wpath] {
277
- pathsToRemove = append(pathsToRemove, wpath)
278
- }
279
- w.ewmut.Unlock()
280
- }
281
- }
282
- w.pmut.Unlock()
283
- for _, p := range pathsToRemove {
284
- // Since these are internal, not much sense in propagating error
285
- // to the user, as that will just confuse them with an error about
286
- // a path they did not explicitly watch themselves.
287
- w.removeWatch(p)
288
- }
289
- }
290
-
291
- return nil
292
-}
293
-
294
-// readEvents reads from the kqueue file descriptor, converts the
295
-// received events into Event objects and sends them via the Event channel
296
-func (w *Watcher) readEvents() {
297
- var (
298
- eventbuf [10]syscall.Kevent_t // Event buffer
299
- events []syscall.Kevent_t // Received events
300
- twait *syscall.Timespec // Time to block waiting for events
301
- n int // Number of events returned from kevent
302
- errno error // Syscall errno
303
- )
304
- events = eventbuf[0:0]
305
- twait = new(syscall.Timespec)
306
- *twait = syscall.NsecToTimespec(keventWaitTime)
307
-
308
- for {
309
- // See if there is a message on the "done" channel
310
- var done bool
311
- select {
312
- case done = <-w.done:
313
- default:
314
- }
315
-
316
- // If "done" message is received
317
- if done {
318
- errno := syscall.Close(w.kq)
319
- if errno != nil {
320
- w.Error <- os.NewSyscallError("close", errno)
321
- }
322
- close(w.internalEvent)
323
- close(w.Error)
324
- return
325
- }
326
-
327
- // Get new events
328
- if len(events) == 0 {
329
- n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait)
330
-
331
- // EINTR is okay, basically the syscall was interrupted before
332
- // timeout expired.
333
- if errno != nil && errno != syscall.EINTR {
334
- w.Error <- os.NewSyscallError("kevent", errno)
335
- continue
336
- }
337
-
338
- // Received some events
339
- if n > 0 {
340
- events = eventbuf[0:n]
341
- }
342
- }
343
-
344
- // Flush the events we received to the events channel
345
- for len(events) > 0 {
346
- fileEvent := new(FileEvent)
347
- watchEvent := &events[0]
348
- fileEvent.mask = uint32(watchEvent.Fflags)
349
- w.pmut.Lock()
350
- fileEvent.Name = w.paths[int(watchEvent.Ident)]
351
- fileInfo := w.finfo[int(watchEvent.Ident)]
352
- w.pmut.Unlock()
353
- if fileInfo != nil && fileInfo.IsDir() && !fileEvent.IsDelete() {
354
- // Double check to make sure the directory exist. This can happen when
355
- // we do a rm -fr on a recursively watched folders and we receive a
356
- // modification event first but the folder has been deleted and later
357
- // receive the delete event
358
- if _, err := os.Lstat(fileEvent.Name); os.IsNotExist(err) {
359
- // mark is as delete event
360
- fileEvent.mask |= sys_NOTE_DELETE
361
- }
362
- }
363
-
364
- if fileInfo != nil && fileInfo.IsDir() && fileEvent.IsModify() && !fileEvent.IsDelete() {
365
- w.sendDirectoryChangeEvents(fileEvent.Name)
366
- } else {
367
- // Send the event on the events channel
368
- w.internalEvent <- fileEvent
369
- }
370
-
371
- // Move to next event
372
- events = events[1:]
373
-
374
- if fileEvent.IsRename() {
375
- w.removeWatch(fileEvent.Name)
376
- w.femut.Lock()
377
- delete(w.fileExists, fileEvent.Name)
378
- w.femut.Unlock()
379
- }
380
- if fileEvent.IsDelete() {
381
- w.removeWatch(fileEvent.Name)
382
- w.femut.Lock()
383
- delete(w.fileExists, fileEvent.Name)
384
- w.femut.Unlock()
385
-
386
- // Look for a file that may have overwritten this
387
- // (ie mv f1 f2 will delete f2 then create f2)
388
- fileDir, _ := filepath.Split(fileEvent.Name)
389
- fileDir = filepath.Clean(fileDir)
390
- w.wmut.Lock()
391
- _, found := w.watches[fileDir]
392
- w.wmut.Unlock()
393
- if found {
394
- // make sure the directory exist before we watch for changes. When we
395
- // do a recursive watch and perform rm -fr, the parent directory might
396
- // have gone missing, ignore the missing directory and let the
397
- // upcoming delete event remove the watch form the parent folder
398
- if _, err := os.Lstat(fileDir); !os.IsNotExist(err) {
399
- w.sendDirectoryChangeEvents(fileDir)
400
- }
401
- }
402
- }
403
- }
404
- }
405
-}
406
-
407
-func (w *Watcher) watchDirectoryFiles(dirPath string) error {
408
- // Get all files
409
- files, err := ioutil.ReadDir(dirPath)
410
- if err != nil {
411
- return err
412
- }
413
-
414
- // Search for new files
415
- for _, fileInfo := range files {
416
- filePath := filepath.Join(dirPath, fileInfo.Name())
417
-
418
- // Inherit fsnFlags from parent directory
419
- w.fsnmut.Lock()
420
- if flags, found := w.fsnFlags[dirPath]; found {
421
- w.fsnFlags[filePath] = flags
422
- } else {
423
- w.fsnFlags[filePath] = FSN_ALL
424
- }
425
- w.fsnmut.Unlock()
426
-
427
- if fileInfo.IsDir() == false {
428
- // Watch file to mimic linux fsnotify
429
- e := w.addWatch(filePath, sys_NOTE_ALLEVENTS)
430
- if e != nil {
431
- return e
432
- }
433
- } else {
434
- // If the user is currently watching directory
435
- // we want to preserve the flags used
436
- w.enmut.Lock()
437
- currFlags, found := w.enFlags[filePath]
438
- w.enmut.Unlock()
439
- var newFlags uint32 = sys_NOTE_DELETE
440
- if found {
441
- newFlags |= currFlags
442
- }
443
-
444
- // Linux gives deletes if not explicitly watching
445
- e := w.addWatch(filePath, newFlags)
446
- if e != nil {
447
- return e
448
- }
449
- }
450
- w.femut.Lock()
451
- w.fileExists[filePath] = true
452
- w.femut.Unlock()
453
- }
454
-
455
- return nil
456
-}
457
-
458
-// sendDirectoryEvents searches the directory for newly created files
459
-// and sends them over the event channel. This functionality is to have
460
-// the BSD version of fsnotify match linux fsnotify which provides a
461
-// create event for files created in a watched directory.
462
-func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
463
- // Get all files
464
- files, err := ioutil.ReadDir(dirPath)
465
- if err != nil {
466
- w.Error <- err
467
- }
468
-
469
- // Search for new files
470
- for _, fileInfo := range files {
471
- filePath := filepath.Join(dirPath, fileInfo.Name())
472
- w.femut.Lock()
473
- _, doesExist := w.fileExists[filePath]
474
- w.femut.Unlock()
475
- if !doesExist {
476
- // Inherit fsnFlags from parent directory
477
- w.fsnmut.Lock()
478
- if flags, found := w.fsnFlags[dirPath]; found {
479
- w.fsnFlags[filePath] = flags
480
- } else {
481
- w.fsnFlags[filePath] = FSN_ALL
482
- }
483
- w.fsnmut.Unlock()
484
-
485
- // Send create event
486
- fileEvent := new(FileEvent)
487
- fileEvent.Name = filePath
488
- fileEvent.create = true
489
- w.internalEvent <- fileEvent
490
- }
491
- w.femut.Lock()
492
- w.fileExists[filePath] = true
493
- w.femut.Unlock()
494
- }
495
- w.watchDirectoryFiles(dirPath)
496
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_linux.go
deleted
-304
@@ -1,304 +0,0 @@
1
-// Copyright 2010 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build linux
6
-
7
-package fsnotify
8
-
9
-import (
10
- "errors"
11
- "fmt"
12
- "os"
13
- "strings"
14
- "sync"
15
- "syscall"
16
- "unsafe"
17
-)
18
-
19
-const (
20
- // Options for inotify_init() are not exported
21
- // sys_IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
22
- // sys_IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
23
-
24
- // Options for AddWatch
25
- sys_IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
26
- sys_IN_ONESHOT uint32 = syscall.IN_ONESHOT
27
- sys_IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
28
-
29
- // The "sys_IN_MASK_ADD" option is not exported, as AddWatch
30
- // adds it automatically, if there is already a watch for the given path
31
- // sys_IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
32
-
33
- // Events
34
- sys_IN_ACCESS uint32 = syscall.IN_ACCESS
35
- sys_IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
36
- sys_IN_ATTRIB uint32 = syscall.IN_ATTRIB
37
- sys_IN_CLOSE uint32 = syscall.IN_CLOSE
38
- sys_IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
39
- sys_IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
40
- sys_IN_CREATE uint32 = syscall.IN_CREATE
41
- sys_IN_DELETE uint32 = syscall.IN_DELETE
42
- sys_IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
43
- sys_IN_MODIFY uint32 = syscall.IN_MODIFY
44
- sys_IN_MOVE uint32 = syscall.IN_MOVE
45
- sys_IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
46
- sys_IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
47
- sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
48
- sys_IN_OPEN uint32 = syscall.IN_OPEN
49
-
50
- sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF
51
-
52
- // Special events
53
- sys_IN_ISDIR uint32 = syscall.IN_ISDIR
54
- sys_IN_IGNORED uint32 = syscall.IN_IGNORED
55
- sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
56
- sys_IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
57
-)
58
-
59
-type FileEvent struct {
60
- mask uint32 // Mask of events
61
- cookie uint32 // Unique cookie associating related events (for rename(2))
62
- Name string // File name (optional)
63
-}
64
-
65
-// IsCreate reports whether the FileEvent was triggered by a creation
66
-func (e *FileEvent) IsCreate() bool {
67
- return (e.mask&sys_IN_CREATE) == sys_IN_CREATE || (e.mask&sys_IN_MOVED_TO) == sys_IN_MOVED_TO
68
-}
69
-
70
-// IsDelete reports whether the FileEvent was triggered by a delete
71
-func (e *FileEvent) IsDelete() bool {
72
- return (e.mask&sys_IN_DELETE_SELF) == sys_IN_DELETE_SELF || (e.mask&sys_IN_DELETE) == sys_IN_DELETE
73
-}
74
-
75
-// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
76
-func (e *FileEvent) IsModify() bool {
77
- return ((e.mask&sys_IN_MODIFY) == sys_IN_MODIFY || (e.mask&sys_IN_ATTRIB) == sys_IN_ATTRIB)
78
-}
79
-
80
-// IsRename reports whether the FileEvent was triggered by a change name
81
-func (e *FileEvent) IsRename() bool {
82
- return ((e.mask&sys_IN_MOVE_SELF) == sys_IN_MOVE_SELF || (e.mask&sys_IN_MOVED_FROM) == sys_IN_MOVED_FROM)
83
-}
84
-
85
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
86
-func (e *FileEvent) IsAttrib() bool {
87
- return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB
88
-}
89
-
90
-type watch struct {
91
- wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
92
- flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
93
-}
94
-
95
-type Watcher struct {
96
- mu sync.Mutex // Map access
97
- fd int // File descriptor (as returned by the inotify_init() syscall)
98
- watches map[string]*watch // Map of inotify watches (key: path)
99
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
100
- fsnmut sync.Mutex // Protects access to fsnFlags.
101
- paths map[int]string // Map of watched paths (key: watch descriptor)
102
- Error chan error // Errors are sent on this channel
103
- internalEvent chan *FileEvent // Events are queued on this channel
104
- Event chan *FileEvent // Events are returned on this channel
105
- done chan bool // Channel for sending a "quit message" to the reader goroutine
106
- isClosed bool // Set to true when Close() is first called
107
-}
108
-
109
-// NewWatcher creates and returns a new inotify instance using inotify_init(2)
110
-func NewWatcher() (*Watcher, error) {
111
- fd, errno := syscall.InotifyInit()
112
- if fd == -1 {
113
- return nil, os.NewSyscallError("inotify_init", errno)
114
- }
115
- w := &Watcher{
116
- fd: fd,
117
- watches: make(map[string]*watch),
118
- fsnFlags: make(map[string]uint32),
119
- paths: make(map[int]string),
120
- internalEvent: make(chan *FileEvent),
121
- Event: make(chan *FileEvent),
122
- Error: make(chan error),
123
- done: make(chan bool, 1),
124
- }
125
-
126
- go w.readEvents()
127
- go w.purgeEvents()
128
- return w, nil
129
-}
130
-
131
-// Close closes an inotify watcher instance
132
-// It sends a message to the reader goroutine to quit and removes all watches
133
-// associated with the inotify instance
134
-func (w *Watcher) Close() error {
135
- if w.isClosed {
136
- return nil
137
- }
138
- w.isClosed = true
139
-
140
- // Remove all watches
141
- for path := range w.watches {
142
- w.RemoveWatch(path)
143
- }
144
-
145
- // Send "quit" message to the reader goroutine
146
- w.done <- true
147
-
148
- return nil
149
-}
150
-
151
-// AddWatch adds path to the watched file set.
152
-// The flags are interpreted as described in inotify_add_watch(2).
153
-func (w *Watcher) addWatch(path string, flags uint32) error {
154
- if w.isClosed {
155
- return errors.New("inotify instance already closed")
156
- }
157
-
158
- w.mu.Lock()
159
- watchEntry, found := w.watches[path]
160
- w.mu.Unlock()
161
- if found {
162
- watchEntry.flags |= flags
163
- flags |= syscall.IN_MASK_ADD
164
- }
165
- wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
166
- if wd == -1 {
167
- return errno
168
- }
169
-
170
- w.mu.Lock()
171
- w.watches[path] = &watch{wd: uint32(wd), flags: flags}
172
- w.paths[wd] = path
173
- w.mu.Unlock()
174
-
175
- return nil
176
-}
177
-
178
-// Watch adds path to the watched file set, watching all events.
179
-func (w *Watcher) watch(path string) error {
180
- return w.addWatch(path, sys_AGNOSTIC_EVENTS)
181
-}
182
-
183
-// RemoveWatch removes path from the watched file set.
184
-func (w *Watcher) removeWatch(path string) error {
185
- w.mu.Lock()
186
- defer w.mu.Unlock()
187
- watch, ok := w.watches[path]
188
- if !ok {
189
- return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
190
- }
191
- success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
192
- if success == -1 {
193
- return os.NewSyscallError("inotify_rm_watch", errno)
194
- }
195
- delete(w.watches, path)
196
- return nil
197
-}
198
-
199
-// readEvents reads from the inotify file descriptor, converts the
200
-// received events into Event objects and sends them via the Event channel
201
-func (w *Watcher) readEvents() {
202
- var (
203
- buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
204
- n int // Number of bytes read with read()
205
- errno error // Syscall errno
206
- )
207
-
208
- for {
209
- // See if there is a message on the "done" channel
210
- select {
211
- case <-w.done:
212
- syscall.Close(w.fd)
213
- close(w.internalEvent)
214
- close(w.Error)
215
- return
216
- default:
217
- }
218
-
219
- n, errno = syscall.Read(w.fd, buf[:])
220
-
221
- // If EOF is received
222
- if n == 0 {
223
- syscall.Close(w.fd)
224
- close(w.internalEvent)
225
- close(w.Error)
226
- return
227
- }
228
-
229
- if n < 0 {
230
- w.Error <- os.NewSyscallError("read", errno)
231
- continue
232
- }
233
- if n < syscall.SizeofInotifyEvent {
234
- w.Error <- errors.New("inotify: short read in readEvents()")
235
- continue
236
- }
237
-
238
- var offset uint32 = 0
239
- // We don't know how many events we just read into the buffer
240
- // While the offset points to at least one whole event...
241
- for offset <= uint32(n-syscall.SizeofInotifyEvent) {
242
- // Point "raw" to the event in the buffer
243
- raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
244
- event := new(FileEvent)
245
- event.mask = uint32(raw.Mask)
246
- event.cookie = uint32(raw.Cookie)
247
- nameLen := uint32(raw.Len)
248
- // If the event happened to the watched directory or the watched file, the kernel
249
- // doesn't append the filename to the event, but we would like to always fill the
250
- // the "Name" field with a valid filename. We retrieve the path of the watch from
251
- // the "paths" map.
252
- w.mu.Lock()
253
- event.Name = w.paths[int(raw.Wd)]
254
- w.mu.Unlock()
255
- watchedName := event.Name
256
- if nameLen > 0 {
257
- // Point "bytes" at the first byte of the filename
258
- bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
259
- // The filename is padded with NUL bytes. TrimRight() gets rid of those.
260
- event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
261
- }
262
-
263
- // Send the events that are not ignored on the events channel
264
- if !event.ignoreLinux() {
265
- // Setup FSNotify flags (inherit from directory watch)
266
- w.fsnmut.Lock()
267
- if _, fsnFound := w.fsnFlags[event.Name]; !fsnFound {
268
- if fsnFlags, watchFound := w.fsnFlags[watchedName]; watchFound {
269
- w.fsnFlags[event.Name] = fsnFlags
270
- } else {
271
- w.fsnFlags[event.Name] = FSN_ALL
272
- }
273
- }
274
- w.fsnmut.Unlock()
275
-
276
- w.internalEvent <- event
277
- }
278
-
279
- // Move to the next event in the buffer
280
- offset += syscall.SizeofInotifyEvent + nameLen
281
- }
282
- }
283
-}
284
-
285
-// Certain types of events can be "ignored" and not sent over the Event
286
-// channel. Such as events marked ignore by the kernel, or MODIFY events
287
-// against files that do not exist.
288
-func (e *FileEvent) ignoreLinux() bool {
289
- // Ignore anything the inotify API says to ignore
290
- if e.mask&sys_IN_IGNORED == sys_IN_IGNORED {
291
- return true
292
- }
293
-
294
- // If the event is not a DELETE or RENAME, the file must exist.
295
- // Otherwise the event is ignored.
296
- // *Note*: this was put in place because it was seen that a MODIFY
297
- // event was sent after the DELETE. This ignores that MODIFY and
298
- // assumes a DELETE will come or has come if the file doesn't exist.
299
- if !(e.IsDelete() || e.IsRename()) {
300
- _, statErr := os.Lstat(e.Name)
301
- return os.IsNotExist(statErr)
302
- }
303
- return false
304
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_bsd.go
deleted
-11
@@ -1,11 +0,0 @@
1
-// Copyright 2013 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build freebsd openbsd netbsd
6
-
7
-package fsnotify
8
-
9
-import "syscall"
10
-
11
-const open_FLAGS = syscall.O_NONBLOCK | syscall.O_RDONLY
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_open_darwin.go
deleted
-11
@@ -1,11 +0,0 @@
1
-// Copyright 2013 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build darwin
6
-
7
-package fsnotify
8
-
9
-import "syscall"
10
-
11
-const open_FLAGS = syscall.O_EVTONLY
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_symlink_test.go
deleted
-74
@@ -1,74 +0,0 @@
1
-// Copyright 2010 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build freebsd openbsd netbsd darwin linux
6
-
7
-package fsnotify
8
-
9
-import (
10
- "os"
11
- "path/filepath"
12
- "testing"
13
- "time"
14
-)
15
-
16
-func TestFsnotifyFakeSymlink(t *testing.T) {
17
- watcher := newWatcher(t)
18
-
19
- // Create directory to watch
20
- testDir := tempMkdir(t)
21
- defer os.RemoveAll(testDir)
22
-
23
- var errorsReceived counter
24
- // Receive errors on the error channel on a separate goroutine
25
- go func() {
26
- for errors := range watcher.Error {
27
- t.Logf("Received error: %s", errors)
28
- errorsReceived.increment()
29
- }
30
- }()
31
-
32
- // Count the CREATE events received
33
- var createEventsReceived, otherEventsReceived counter
34
- go func() {
35
- for ev := range watcher.Event {
36
- t.Logf("event received: %s", ev)
37
- if ev.IsCreate() {
38
- createEventsReceived.increment()
39
- } else {
40
- otherEventsReceived.increment()
41
- }
42
- }
43
- }()
44
-
45
- addWatch(t, watcher, testDir)
46
-
47
- if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil {
48
- t.Fatalf("Failed to create bogus symlink: %s", err)
49
- }
50
- t.Logf("Created bogus symlink")
51
-
52
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
53
- time.Sleep(500 * time.Millisecond)
54
-
55
- // Should not be error, just no events for broken links (watching nothing)
56
- if errorsReceived.value() > 0 {
57
- t.Fatal("fsnotify errors have been received.")
58
- }
59
- if otherEventsReceived.value() > 0 {
60
- t.Fatal("fsnotify other events received on the broken link")
61
- }
62
-
63
- // Except for 1 create event (for the link itself)
64
- if createEventsReceived.value() == 0 {
65
- t.Fatal("fsnotify create events were not received after 500 ms")
66
- }
67
- if createEventsReceived.value() > 1 {
68
- t.Fatal("fsnotify more create events received than expected")
69
- }
70
-
71
- // Try closing the fsnotify instance
72
- t.Log("calling Close()")
73
- watcher.Close()
74
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_test.go
deleted
-1010
@@ -1,1010 +0,0 @@
1
-// Copyright 2010 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package fsnotify
6
-
7
-import (
8
- "io/ioutil"
9
- "os"
10
- "os/exec"
11
- "path/filepath"
12
- "runtime"
13
- "sync/atomic"
14
- "testing"
15
- "time"
16
-)
17
-
18
-// An atomic counter
19
-type counter struct {
20
- val int32
21
-}
22
-
23
-func (c *counter) increment() {
24
- atomic.AddInt32(&c.val, 1)
25
-}
26
-
27
-func (c *counter) value() int32 {
28
- return atomic.LoadInt32(&c.val)
29
-}
30
-
31
-func (c *counter) reset() {
32
- atomic.StoreInt32(&c.val, 0)
33
-}
34
-
35
-// tempMkdir makes a temporary directory
36
-func tempMkdir(t *testing.T) string {
37
- dir, err := ioutil.TempDir("", "fsnotify")
38
- if err != nil {
39
- t.Fatalf("failed to create test directory: %s", err)
40
- }
41
- return dir
42
-}
43
-
44
-// newWatcher initializes an fsnotify Watcher instance.
45
-func newWatcher(t *testing.T) *Watcher {
46
- watcher, err := NewWatcher()
47
- if err != nil {
48
- t.Fatalf("NewWatcher() failed: %s", err)
49
- }
50
- return watcher
51
-}
52
-
53
-// addWatch adds a watch for a directory
54
-func addWatch(t *testing.T, watcher *Watcher, dir string) {
55
- if err := watcher.Watch(dir); err != nil {
56
- t.Fatalf("watcher.Watch(%q) failed: %s", dir, err)
57
- }
58
-}
59
-
60
-func TestFsnotifyMultipleOperations(t *testing.T) {
61
- watcher := newWatcher(t)
62
-
63
- // Receive errors on the error channel on a separate goroutine
64
- go func() {
65
- for err := range watcher.Error {
66
- t.Fatalf("error received: %s", err)
67
- }
68
- }()
69
-
70
- // Create directory to watch
71
- testDir := tempMkdir(t)
72
- defer os.RemoveAll(testDir)
73
-
74
- // Create directory that's not watched
75
- testDirToMoveFiles := tempMkdir(t)
76
- defer os.RemoveAll(testDirToMoveFiles)
77
-
78
- testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
79
- testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile")
80
-
81
- addWatch(t, watcher, testDir)
82
-
83
- // Receive events on the event channel on a separate goroutine
84
- eventstream := watcher.Event
85
- var createReceived, modifyReceived, deleteReceived, renameReceived counter
86
- done := make(chan bool)
87
- go func() {
88
- for event := range eventstream {
89
- // Only count relevant events
90
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
91
- t.Logf("event received: %s", event)
92
- if event.IsDelete() {
93
- deleteReceived.increment()
94
- }
95
- if event.IsModify() {
96
- modifyReceived.increment()
97
- }
98
- if event.IsCreate() {
99
- createReceived.increment()
100
- }
101
- if event.IsRename() {
102
- renameReceived.increment()
103
- }
104
- } else {
105
- t.Logf("unexpected event received: %s", event)
106
- }
107
- }
108
- done <- true
109
- }()
110
-
111
- // Create a file
112
- // This should add at least one event to the fsnotify event queue
113
- var f *os.File
114
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
115
- if err != nil {
116
- t.Fatalf("creating test file failed: %s", err)
117
- }
118
- f.Sync()
119
-
120
- time.Sleep(time.Millisecond)
121
- f.WriteString("data")
122
- f.Sync()
123
- f.Close()
124
-
125
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
126
-
127
- if err := testRename(testFile, testFileRenamed); err != nil {
128
- t.Fatalf("rename failed: %s", err)
129
- }
130
-
131
- // Modify the file outside of the watched dir
132
- f, err = os.Open(testFileRenamed)
133
- if err != nil {
134
- t.Fatalf("open test renamed file failed: %s", err)
135
- }
136
- f.WriteString("data")
137
- f.Sync()
138
- f.Close()
139
-
140
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
141
-
142
- // Recreate the file that was moved
143
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
144
- if err != nil {
145
- t.Fatalf("creating test file failed: %s", err)
146
- }
147
- f.Close()
148
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
149
-
150
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
151
- time.Sleep(500 * time.Millisecond)
152
- cReceived := createReceived.value()
153
- if cReceived != 2 {
154
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
155
- }
156
- mReceived := modifyReceived.value()
157
- if mReceived != 1 {
158
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
159
- }
160
- dReceived := deleteReceived.value()
161
- rReceived := renameReceived.value()
162
- if dReceived+rReceived != 1 {
163
- t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1)
164
- }
165
-
166
- // Try closing the fsnotify instance
167
- t.Log("calling Close()")
168
- watcher.Close()
169
- t.Log("waiting for the event channel to become closed...")
170
- select {
171
- case <-done:
172
- t.Log("event channel closed")
173
- case <-time.After(2 * time.Second):
174
- t.Fatal("event stream was not closed after 2 seconds")
175
- }
176
-}
177
-
178
-func TestFsnotifyMultipleCreates(t *testing.T) {
179
- watcher := newWatcher(t)
180
-
181
- // Receive errors on the error channel on a separate goroutine
182
- go func() {
183
- for err := range watcher.Error {
184
- t.Fatalf("error received: %s", err)
185
- }
186
- }()
187
-
188
- // Create directory to watch
189
- testDir := tempMkdir(t)
190
- defer os.RemoveAll(testDir)
191
-
192
- testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
193
-
194
- addWatch(t, watcher, testDir)
195
-
196
- // Receive events on the event channel on a separate goroutine
197
- eventstream := watcher.Event
198
- var createReceived, modifyReceived, deleteReceived counter
199
- done := make(chan bool)
200
- go func() {
201
- for event := range eventstream {
202
- // Only count relevant events
203
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
204
- t.Logf("event received: %s", event)
205
- if event.IsDelete() {
206
- deleteReceived.increment()
207
- }
208
- if event.IsCreate() {
209
- createReceived.increment()
210
- }
211
- if event.IsModify() {
212
- modifyReceived.increment()
213
- }
214
- } else {
215
- t.Logf("unexpected event received: %s", event)
216
- }
217
- }
218
- done <- true
219
- }()
220
-
221
- // Create a file
222
- // This should add at least one event to the fsnotify event queue
223
- var f *os.File
224
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
225
- if err != nil {
226
- t.Fatalf("creating test file failed: %s", err)
227
- }
228
- f.Sync()
229
-
230
- time.Sleep(time.Millisecond)
231
- f.WriteString("data")
232
- f.Sync()
233
- f.Close()
234
-
235
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
236
-
237
- os.Remove(testFile)
238
-
239
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
240
-
241
- // Recreate the file
242
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
243
- if err != nil {
244
- t.Fatalf("creating test file failed: %s", err)
245
- }
246
- f.Close()
247
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
248
-
249
- // Modify
250
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
251
- if err != nil {
252
- t.Fatalf("creating test file failed: %s", err)
253
- }
254
- f.Sync()
255
-
256
- time.Sleep(time.Millisecond)
257
- f.WriteString("data")
258
- f.Sync()
259
- f.Close()
260
-
261
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
262
-
263
- // Modify
264
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
265
- if err != nil {
266
- t.Fatalf("creating test file failed: %s", err)
267
- }
268
- f.Sync()
269
-
270
- time.Sleep(time.Millisecond)
271
- f.WriteString("data")
272
- f.Sync()
273
- f.Close()
274
-
275
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
276
-
277
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
278
- time.Sleep(500 * time.Millisecond)
279
- cReceived := createReceived.value()
280
- if cReceived != 2 {
281
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
282
- }
283
- mReceived := modifyReceived.value()
284
- if mReceived < 3 {
285
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3)
286
- }
287
- dReceived := deleteReceived.value()
288
- if dReceived != 1 {
289
- t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1)
290
- }
291
-
292
- // Try closing the fsnotify instance
293
- t.Log("calling Close()")
294
- watcher.Close()
295
- t.Log("waiting for the event channel to become closed...")
296
- select {
297
- case <-done:
298
- t.Log("event channel closed")
299
- case <-time.After(2 * time.Second):
300
- t.Fatal("event stream was not closed after 2 seconds")
301
- }
302
-}
303
-
304
-func TestFsnotifyDirOnly(t *testing.T) {
305
- watcher := newWatcher(t)
306
-
307
- // Create directory to watch
308
- testDir := tempMkdir(t)
309
- defer os.RemoveAll(testDir)
310
-
311
- // Create a file before watching directory
312
- // This should NOT add any events to the fsnotify event queue
313
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
314
- {
315
- var f *os.File
316
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
317
- if err != nil {
318
- t.Fatalf("creating test file failed: %s", err)
319
- }
320
- f.Sync()
321
- f.Close()
322
- }
323
-
324
- addWatch(t, watcher, testDir)
325
-
326
- // Receive errors on the error channel on a separate goroutine
327
- go func() {
328
- for err := range watcher.Error {
329
- t.Fatalf("error received: %s", err)
330
- }
331
- }()
332
-
333
- testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile")
334
-
335
- // Receive events on the event channel on a separate goroutine
336
- eventstream := watcher.Event
337
- var createReceived, modifyReceived, deleteReceived counter
338
- done := make(chan bool)
339
- go func() {
340
- for event := range eventstream {
341
- // Only count relevant events
342
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) {
343
- t.Logf("event received: %s", event)
344
- if event.IsDelete() {
345
- deleteReceived.increment()
346
- }
347
- if event.IsModify() {
348
- modifyReceived.increment()
349
- }
350
- if event.IsCreate() {
351
- createReceived.increment()
352
- }
353
- } else {
354
- t.Logf("unexpected event received: %s", event)
355
- }
356
- }
357
- done <- true
358
- }()
359
-
360
- // Create a file
361
- // This should add at least one event to the fsnotify event queue
362
- var f *os.File
363
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
364
- if err != nil {
365
- t.Fatalf("creating test file failed: %s", err)
366
- }
367
- f.Sync()
368
-
369
- time.Sleep(time.Millisecond)
370
- f.WriteString("data")
371
- f.Sync()
372
- f.Close()
373
-
374
- time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
375
-
376
- os.Remove(testFile)
377
- os.Remove(testFileAlreadyExists)
378
-
379
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
380
- time.Sleep(500 * time.Millisecond)
381
- cReceived := createReceived.value()
382
- if cReceived != 1 {
383
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1)
384
- }
385
- mReceived := modifyReceived.value()
386
- if mReceived != 1 {
387
- t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
388
- }
389
- dReceived := deleteReceived.value()
390
- if dReceived != 2 {
391
- t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
392
- }
393
-
394
- // Try closing the fsnotify instance
395
- t.Log("calling Close()")
396
- watcher.Close()
397
- t.Log("waiting for the event channel to become closed...")
398
- select {
399
- case <-done:
400
- t.Log("event channel closed")
401
- case <-time.After(2 * time.Second):
402
- t.Fatal("event stream was not closed after 2 seconds")
403
- }
404
-}
405
-
406
-func TestFsnotifyDeleteWatchedDir(t *testing.T) {
407
- watcher := newWatcher(t)
408
- defer watcher.Close()
409
-
410
- // Create directory to watch
411
- testDir := tempMkdir(t)
412
- defer os.RemoveAll(testDir)
413
-
414
- // Create a file before watching directory
415
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
416
- {
417
- var f *os.File
418
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
419
- if err != nil {
420
- t.Fatalf("creating test file failed: %s", err)
421
- }
422
- f.Sync()
423
- f.Close()
424
- }
425
-
426
- addWatch(t, watcher, testDir)
427
-
428
- // Add a watch for testFile
429
- addWatch(t, watcher, testFileAlreadyExists)
430
-
431
- // Receive errors on the error channel on a separate goroutine
432
- go func() {
433
- for err := range watcher.Error {
434
- t.Fatalf("error received: %s", err)
435
- }
436
- }()
437
-
438
- // Receive events on the event channel on a separate goroutine
439
- eventstream := watcher.Event
440
- var deleteReceived counter
441
- go func() {
442
- for event := range eventstream {
443
- // Only count relevant events
444
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) {
445
- t.Logf("event received: %s", event)
446
- if event.IsDelete() {
447
- deleteReceived.increment()
448
- }
449
- } else {
450
- t.Logf("unexpected event received: %s", event)
451
- }
452
- }
453
- }()
454
-
455
- os.RemoveAll(testDir)
456
-
457
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
458
- time.Sleep(500 * time.Millisecond)
459
- dReceived := deleteReceived.value()
460
- if dReceived < 2 {
461
- t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived)
462
- }
463
-}
464
-
465
-func TestFsnotifySubDir(t *testing.T) {
466
- watcher := newWatcher(t)
467
-
468
- // Create directory to watch
469
- testDir := tempMkdir(t)
470
- defer os.RemoveAll(testDir)
471
-
472
- testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile")
473
- testSubDir := filepath.Join(testDir, "sub")
474
- testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile")
475
-
476
- // Receive errors on the error channel on a separate goroutine
477
- go func() {
478
- for err := range watcher.Error {
479
- t.Fatalf("error received: %s", err)
480
- }
481
- }()
482
-
483
- // Receive events on the event channel on a separate goroutine
484
- eventstream := watcher.Event
485
- var createReceived, deleteReceived counter
486
- done := make(chan bool)
487
- go func() {
488
- for event := range eventstream {
489
- // Only count relevant events
490
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) {
491
- t.Logf("event received: %s", event)
492
- if event.IsCreate() {
493
- createReceived.increment()
494
- }
495
- if event.IsDelete() {
496
- deleteReceived.increment()
497
- }
498
- } else {
499
- t.Logf("unexpected event received: %s", event)
500
- }
501
- }
502
- done <- true
503
- }()
504
-
505
- addWatch(t, watcher, testDir)
506
-
507
- // Create sub-directory
508
- if err := os.Mkdir(testSubDir, 0777); err != nil {
509
- t.Fatalf("failed to create test sub-directory: %s", err)
510
- }
511
-
512
- // Create a file
513
- var f *os.File
514
- f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666)
515
- if err != nil {
516
- t.Fatalf("creating test file failed: %s", err)
517
- }
518
- f.Sync()
519
- f.Close()
520
-
521
- // Create a file (Should not see this! we are not watching subdir)
522
- var fs *os.File
523
- fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666)
524
- if err != nil {
525
- t.Fatalf("creating test file failed: %s", err)
526
- }
527
- fs.Sync()
528
- fs.Close()
529
-
530
- time.Sleep(200 * time.Millisecond)
531
-
532
- // Make sure receive deletes for both file and sub-directory
533
- os.RemoveAll(testSubDir)
534
- os.Remove(testFile1)
535
-
536
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
537
- time.Sleep(500 * time.Millisecond)
538
- cReceived := createReceived.value()
539
- if cReceived != 2 {
540
- t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
541
- }
542
- dReceived := deleteReceived.value()
543
- if dReceived != 2 {
544
- t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
545
- }
546
-
547
- // Try closing the fsnotify instance
548
- t.Log("calling Close()")
549
- watcher.Close()
550
- t.Log("waiting for the event channel to become closed...")
551
- select {
552
- case <-done:
553
- t.Log("event channel closed")
554
- case <-time.After(2 * time.Second):
555
- t.Fatal("event stream was not closed after 2 seconds")
556
- }
557
-}
558
-
559
-func TestFsnotifyRename(t *testing.T) {
560
- watcher := newWatcher(t)
561
-
562
- // Create directory to watch
563
- testDir := tempMkdir(t)
564
- defer os.RemoveAll(testDir)
565
-
566
- addWatch(t, watcher, testDir)
567
-
568
- // Receive errors on the error channel on a separate goroutine
569
- go func() {
570
- for err := range watcher.Error {
571
- t.Fatalf("error received: %s", err)
572
- }
573
- }()
574
-
575
- testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile")
576
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
577
-
578
- // Receive events on the event channel on a separate goroutine
579
- eventstream := watcher.Event
580
- var renameReceived counter
581
- done := make(chan bool)
582
- go func() {
583
- for event := range eventstream {
584
- // Only count relevant events
585
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
586
- if event.IsRename() {
587
- renameReceived.increment()
588
- }
589
- t.Logf("event received: %s", event)
590
- } else {
591
- t.Logf("unexpected event received: %s", event)
592
- }
593
- }
594
- done <- true
595
- }()
596
-
597
- // Create a file
598
- // This should add at least one event to the fsnotify event queue
599
- var f *os.File
600
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
601
- if err != nil {
602
- t.Fatalf("creating test file failed: %s", err)
603
- }
604
- f.Sync()
605
-
606
- f.WriteString("data")
607
- f.Sync()
608
- f.Close()
609
-
610
- // Add a watch for testFile
611
- addWatch(t, watcher, testFile)
612
-
613
- if err := testRename(testFile, testFileRenamed); err != nil {
614
- t.Fatalf("rename failed: %s", err)
615
- }
616
-
617
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
618
- time.Sleep(500 * time.Millisecond)
619
- if renameReceived.value() == 0 {
620
- t.Fatal("fsnotify rename events have not been received after 500 ms")
621
- }
622
-
623
- // Try closing the fsnotify instance
624
- t.Log("calling Close()")
625
- watcher.Close()
626
- t.Log("waiting for the event channel to become closed...")
627
- select {
628
- case <-done:
629
- t.Log("event channel closed")
630
- case <-time.After(2 * time.Second):
631
- t.Fatal("event stream was not closed after 2 seconds")
632
- }
633
-
634
- os.Remove(testFileRenamed)
635
-}
636
-
637
-func TestFsnotifyRenameToCreate(t *testing.T) {
638
- watcher := newWatcher(t)
639
-
640
- // Create directory to watch
641
- testDir := tempMkdir(t)
642
- defer os.RemoveAll(testDir)
643
-
644
- // Create directory to get file
645
- testDirFrom := tempMkdir(t)
646
- defer os.RemoveAll(testDirFrom)
647
-
648
- addWatch(t, watcher, testDir)
649
-
650
- // Receive errors on the error channel on a separate goroutine
651
- go func() {
652
- for err := range watcher.Error {
653
- t.Fatalf("error received: %s", err)
654
- }
655
- }()
656
-
657
- testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
658
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
659
-
660
- // Receive events on the event channel on a separate goroutine
661
- eventstream := watcher.Event
662
- var createReceived counter
663
- done := make(chan bool)
664
- go func() {
665
- for event := range eventstream {
666
- // Only count relevant events
667
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
668
- if event.IsCreate() {
669
- createReceived.increment()
670
- }
671
- t.Logf("event received: %s", event)
672
- } else {
673
- t.Logf("unexpected event received: %s", event)
674
- }
675
- }
676
- done <- true
677
- }()
678
-
679
- // Create a file
680
- // This should add at least one event to the fsnotify event queue
681
- var f *os.File
682
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
683
- if err != nil {
684
- t.Fatalf("creating test file failed: %s", err)
685
- }
686
- f.Sync()
687
- f.Close()
688
-
689
- if err := testRename(testFile, testFileRenamed); err != nil {
690
- t.Fatalf("rename failed: %s", err)
691
- }
692
-
693
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
694
- time.Sleep(500 * time.Millisecond)
695
- if createReceived.value() == 0 {
696
- t.Fatal("fsnotify create events have not been received after 500 ms")
697
- }
698
-
699
- // Try closing the fsnotify instance
700
- t.Log("calling Close()")
701
- watcher.Close()
702
- t.Log("waiting for the event channel to become closed...")
703
- select {
704
- case <-done:
705
- t.Log("event channel closed")
706
- case <-time.After(2 * time.Second):
707
- t.Fatal("event stream was not closed after 2 seconds")
708
- }
709
-
710
- os.Remove(testFileRenamed)
711
-}
712
-
713
-func TestFsnotifyRenameToOverwrite(t *testing.T) {
714
- switch runtime.GOOS {
715
- case "plan9", "windows":
716
- t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS)
717
- }
718
-
719
- watcher := newWatcher(t)
720
-
721
- // Create directory to watch
722
- testDir := tempMkdir(t)
723
- defer os.RemoveAll(testDir)
724
-
725
- // Create directory to get file
726
- testDirFrom := tempMkdir(t)
727
- defer os.RemoveAll(testDirFrom)
728
-
729
- testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
730
- testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
731
-
732
- // Create a file
733
- var fr *os.File
734
- fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666)
735
- if err != nil {
736
- t.Fatalf("creating test file failed: %s", err)
737
- }
738
- fr.Sync()
739
- fr.Close()
740
-
741
- addWatch(t, watcher, testDir)
742
-
743
- // Receive errors on the error channel on a separate goroutine
744
- go func() {
745
- for err := range watcher.Error {
746
- t.Fatalf("error received: %s", err)
747
- }
748
- }()
749
-
750
- // Receive events on the event channel on a separate goroutine
751
- eventstream := watcher.Event
752
- var eventReceived counter
753
- done := make(chan bool)
754
- go func() {
755
- for event := range eventstream {
756
- // Only count relevant events
757
- if event.Name == filepath.Clean(testFileRenamed) {
758
- eventReceived.increment()
759
- t.Logf("event received: %s", event)
760
- } else {
761
- t.Logf("unexpected event received: %s", event)
762
- }
763
- }
764
- done <- true
765
- }()
766
-
767
- // Create a file
768
- // This should add at least one event to the fsnotify event queue
769
- var f *os.File
770
- f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
771
- if err != nil {
772
- t.Fatalf("creating test file failed: %s", err)
773
- }
774
- f.Sync()
775
- f.Close()
776
-
777
- if err := testRename(testFile, testFileRenamed); err != nil {
778
- t.Fatalf("rename failed: %s", err)
779
- }
780
-
781
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
782
- time.Sleep(500 * time.Millisecond)
783
- if eventReceived.value() == 0 {
784
- t.Fatal("fsnotify events have not been received after 500 ms")
785
- }
786
-
787
- // Try closing the fsnotify instance
788
- t.Log("calling Close()")
789
- watcher.Close()
790
- t.Log("waiting for the event channel to become closed...")
791
- select {
792
- case <-done:
793
- t.Log("event channel closed")
794
- case <-time.After(2 * time.Second):
795
- t.Fatal("event stream was not closed after 2 seconds")
796
- }
797
-
798
- os.Remove(testFileRenamed)
799
-}
800
-
801
-func TestRemovalOfWatch(t *testing.T) {
802
- // Create directory to watch
803
- testDir := tempMkdir(t)
804
- defer os.RemoveAll(testDir)
805
-
806
- // Create a file before watching directory
807
- testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
808
- {
809
- var f *os.File
810
- f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
811
- if err != nil {
812
- t.Fatalf("creating test file failed: %s", err)
813
- }
814
- f.Sync()
815
- f.Close()
816
- }
817
-
818
- watcher := newWatcher(t)
819
- defer watcher.Close()
820
-
821
- addWatch(t, watcher, testDir)
822
- if err := watcher.RemoveWatch(testDir); err != nil {
823
- t.Fatalf("Could not remove the watch: %v\n", err)
824
- }
825
-
826
- go func() {
827
- select {
828
- case ev := <-watcher.Event:
829
- t.Fatalf("We received event: %v\n", ev)
830
- case <-time.After(500 * time.Millisecond):
831
- t.Log("No event received, as expected.")
832
- }
833
- }()
834
-
835
- time.Sleep(200 * time.Millisecond)
836
- // Modify the file outside of the watched dir
837
- f, err := os.Open(testFileAlreadyExists)
838
- if err != nil {
839
- t.Fatalf("Open test file failed: %s", err)
840
- }
841
- f.WriteString("data")
842
- f.Sync()
843
- f.Close()
844
- if err := os.Chmod(testFileAlreadyExists, 0700); err != nil {
845
- t.Fatalf("chmod failed: %s", err)
846
- }
847
- time.Sleep(400 * time.Millisecond)
848
-}
849
-
850
-func TestFsnotifyAttrib(t *testing.T) {
851
- if runtime.GOOS == "windows" {
852
- t.Skip("attributes don't work on Windows.")
853
- }
854
-
855
- watcher := newWatcher(t)
856
-
857
- // Create directory to watch
858
- testDir := tempMkdir(t)
859
- defer os.RemoveAll(testDir)
860
-
861
- // Receive errors on the error channel on a separate goroutine
862
- go func() {
863
- for err := range watcher.Error {
864
- t.Fatalf("error received: %s", err)
865
- }
866
- }()
867
-
868
- testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile")
869
-
870
- // Receive events on the event channel on a separate goroutine
871
- eventstream := watcher.Event
872
- // The modifyReceived counter counts IsModify events that are not IsAttrib,
873
- // and the attribReceived counts IsAttrib events (which are also IsModify as
874
- // a consequence).
875
- var modifyReceived counter
876
- var attribReceived counter
877
- done := make(chan bool)
878
- go func() {
879
- for event := range eventstream {
880
- // Only count relevant events
881
- if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
882
- if event.IsModify() {
883
- modifyReceived.increment()
884
- }
885
- if event.IsAttrib() {
886
- attribReceived.increment()
887
- }
888
- t.Logf("event received: %s", event)
889
- } else {
890
- t.Logf("unexpected event received: %s", event)
891
- }
892
- }
893
- done <- true
894
- }()
895
-
896
- // Create a file
897
- // This should add at least one event to the fsnotify event queue
898
- var f *os.File
899
- f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
900
- if err != nil {
901
- t.Fatalf("creating test file failed: %s", err)
902
- }
903
- f.Sync()
904
-
905
- f.WriteString("data")
906
- f.Sync()
907
- f.Close()
908
-
909
- // Add a watch for testFile
910
- addWatch(t, watcher, testFile)
911
-
912
- if err := os.Chmod(testFile, 0700); err != nil {
913
- t.Fatalf("chmod failed: %s", err)
914
- }
915
-
916
- // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
917
- // Creating/writing a file changes also the mtime, so IsAttrib should be set to true here
918
- time.Sleep(500 * time.Millisecond)
919
- if modifyReceived.value() == 0 {
920
- t.Fatal("fsnotify modify events have not received after 500 ms")
921
- }
922
- if attribReceived.value() == 0 {
923
- t.Fatal("fsnotify attribute events have not received after 500 ms")
924
- }
925
-
926
- // Modifying the contents of the file does not set the attrib flag (although eg. the mtime
927
- // might have been modified).
928
- modifyReceived.reset()
929
- attribReceived.reset()
930
-
931
- f, err = os.OpenFile(testFile, os.O_WRONLY, 0)
932
- if err != nil {
933
- t.Fatalf("reopening test file failed: %s", err)
934
- }
935
-
936
- f.WriteString("more data")
937
- f.Sync()
938
- f.Close()
939
-
940
- time.Sleep(500 * time.Millisecond)
941
-
942
- if modifyReceived.value() != 1 {
943
- t.Fatal("didn't receive a modify event after changing test file contents")
944
- }
945
-
946
- if attribReceived.value() != 0 {
947
- t.Fatal("did receive an unexpected attrib event after changing test file contents")
948
- }
949
-
950
- modifyReceived.reset()
951
- attribReceived.reset()
952
-
953
- // Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents
954
- // of the file are not changed though)
955
- if err := os.Chmod(testFile, 0600); err != nil {
956
- t.Fatalf("chmod failed: %s", err)
957
- }
958
-
959
- time.Sleep(500 * time.Millisecond)
960
-
961
- if attribReceived.value() != 1 {
962
- t.Fatal("didn't receive an attribute change after 500ms")
963
- }
964
-
965
- // Try closing the fsnotify instance
966
- t.Log("calling Close()")
967
- watcher.Close()
968
- t.Log("waiting for the event channel to become closed...")
969
- select {
970
- case <-done:
971
- t.Log("event channel closed")
972
- case <-time.After(1e9):
973
- t.Fatal("event stream was not closed after 1 second")
974
- }
975
-
976
- os.Remove(testFile)
977
-}
978
-
979
-func TestFsnotifyClose(t *testing.T) {
980
- watcher := newWatcher(t)
981
- watcher.Close()
982
-
983
- var done int32
984
- go func() {
985
- watcher.Close()
986
- atomic.StoreInt32(&done, 1)
987
- }()
988
-
989
- time.Sleep(50e6) // 50 ms
990
- if atomic.LoadInt32(&done) == 0 {
991
- t.Fatal("double Close() test failed: second Close() call didn't return")
992
- }
993
-
994
- testDir := tempMkdir(t)
995
- defer os.RemoveAll(testDir)
996
-
997
- if err := watcher.Watch(testDir); err == nil {
998
- t.Fatal("expected error on Watch() after Close(), got nil")
999
- }
1000
-}
1001
-
1002
-func testRename(file1, file2 string) error {
1003
- switch runtime.GOOS {
1004
- case "windows", "plan9":
1005
- return os.Rename(file1, file2)
1006
- default:
1007
- cmd := exec.Command("mv", file1, file2)
1008
- return cmd.Run()
1009
- }
1010
-}
Godeps/_workspace/src/github.com/howeyc/fsnotify/fsnotify_windows.go
deleted
-598
@@ -1,598 +0,0 @@
1
-// Copyright 2011 The Go Authors. All rights reserved.
2
-// Use of this source code is governed by a BSD-style
3
-// license that can be found in the LICENSE file.
4
-
5
-// +build windows
6
-
7
-package fsnotify
8
-
9
-import (
10
- "errors"
11
- "fmt"
12
- "os"
13
- "path/filepath"
14
- "runtime"
15
- "sync"
16
- "syscall"
17
- "unsafe"
18
-)
19
-
20
-const (
21
- // Options for AddWatch
22
- sys_FS_ONESHOT = 0x80000000
23
- sys_FS_ONLYDIR = 0x1000000
24
-
25
- // Events
26
- sys_FS_ACCESS = 0x1
27
- sys_FS_ALL_EVENTS = 0xfff
28
- sys_FS_ATTRIB = 0x4
29
- sys_FS_CLOSE = 0x18
30
- sys_FS_CREATE = 0x100
31
- sys_FS_DELETE = 0x200
32
- sys_FS_DELETE_SELF = 0x400
33
- sys_FS_MODIFY = 0x2
34
- sys_FS_MOVE = 0xc0
35
- sys_FS_MOVED_FROM = 0x40
36
- sys_FS_MOVED_TO = 0x80
37
- sys_FS_MOVE_SELF = 0x800
38
-
39
- // Special events
40
- sys_FS_IGNORED = 0x8000
41
- sys_FS_Q_OVERFLOW = 0x4000
42
-)
43
-
44
-const (
45
- // TODO(nj): Use syscall.ERROR_MORE_DATA from ztypes_windows in Go 1.3+
46
- sys_ERROR_MORE_DATA syscall.Errno = 234
47
-)
48
-
49
-// Event is the type of the notification messages
50
-// received on the watcher's Event channel.
51
-type FileEvent struct {
52
- mask uint32 // Mask of events
53
- cookie uint32 // Unique cookie associating related events (for rename)
54
- Name string // File name (optional)
55
-}
56
-
57
-// IsCreate reports whether the FileEvent was triggered by a creation
58
-func (e *FileEvent) IsCreate() bool { return (e.mask & sys_FS_CREATE) == sys_FS_CREATE }
59
-
60
-// IsDelete reports whether the FileEvent was triggered by a delete
61
-func (e *FileEvent) IsDelete() bool {
62
- return ((e.mask&sys_FS_DELETE) == sys_FS_DELETE || (e.mask&sys_FS_DELETE_SELF) == sys_FS_DELETE_SELF)
63
-}
64
-
65
-// IsModify reports whether the FileEvent was triggered by a file modification or attribute change
66
-func (e *FileEvent) IsModify() bool {
67
- return ((e.mask&sys_FS_MODIFY) == sys_FS_MODIFY || (e.mask&sys_FS_ATTRIB) == sys_FS_ATTRIB)
68
-}
69
-
70
-// IsRename reports whether the FileEvent was triggered by a change name
71
-func (e *FileEvent) IsRename() bool {
72
- return ((e.mask&sys_FS_MOVE) == sys_FS_MOVE || (e.mask&sys_FS_MOVE_SELF) == sys_FS_MOVE_SELF || (e.mask&sys_FS_MOVED_FROM) == sys_FS_MOVED_FROM || (e.mask&sys_FS_MOVED_TO) == sys_FS_MOVED_TO)
73
-}
74
-
75
-// IsAttrib reports whether the FileEvent was triggered by a change in the file metadata.
76
-func (e *FileEvent) IsAttrib() bool {
77
- return (e.mask & sys_FS_ATTRIB) == sys_FS_ATTRIB
78
-}
79
-
80
-const (
81
- opAddWatch = iota
82
- opRemoveWatch
83
-)
84
-
85
-const (
86
- provisional uint64 = 1 << (32 + iota)
87
-)
88
-
89
-type input struct {
90
- op int
91
- path string
92
- flags uint32
93
- reply chan error
94
-}
95
-
96
-type inode struct {
97
- handle syscall.Handle
98
- volume uint32
99
- index uint64
100
-}
101
-
102
-type watch struct {
103
- ov syscall.Overlapped
104
- ino *inode // i-number
105
- path string // Directory path
106
- mask uint64 // Directory itself is being watched with these notify flags
107
- names map[string]uint64 // Map of names being watched and their notify flags
108
- rename string // Remembers the old name while renaming a file
109
- buf [4096]byte
110
-}
111
-
112
-type indexMap map[uint64]*watch
113
-type watchMap map[uint32]indexMap
114
-
115
-// A Watcher waits for and receives event notifications
116
-// for a specific set of files and directories.
117
-type Watcher struct {
118
- mu sync.Mutex // Map access
119
- port syscall.Handle // Handle to completion port
120
- watches watchMap // Map of watches (key: i-number)
121
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter
122
- fsnmut sync.Mutex // Protects access to fsnFlags.
123
- input chan *input // Inputs to the reader are sent on this channel
124
- internalEvent chan *FileEvent // Events are queued on this channel
125
- Event chan *FileEvent // Events are returned on this channel
126
- Error chan error // Errors are sent on this channel
127
- isClosed bool // Set to true when Close() is first called
128
- quit chan chan<- error
129
- cookie uint32
130
-}
131
-
132
-// NewWatcher creates and returns a Watcher.
133
-func NewWatcher() (*Watcher, error) {
134
- port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
135
- if e != nil {
136
- return nil, os.NewSyscallError("CreateIoCompletionPort", e)
137
- }
138
- w := &Watcher{
139
- port: port,
140
- watches: make(watchMap),
141
- fsnFlags: make(map[string]uint32),
142
- input: make(chan *input, 1),
143
- Event: make(chan *FileEvent, 50),
144
- internalEvent: make(chan *FileEvent),
145
- Error: make(chan error),
146
- quit: make(chan chan<- error, 1),
147
- }
148
- go w.readEvents()
149
- go w.purgeEvents()
150
- return w, nil
151
-}
152
-
153
-// Close closes a Watcher.
154
-// It sends a message to the reader goroutine to quit and removes all watches
155
-// associated with the watcher.
156
-func (w *Watcher) Close() error {
157
- if w.isClosed {
158
- return nil
159
- }
160
- w.isClosed = true
161
-
162
- // Send "quit" message to the reader goroutine
163
- ch := make(chan error)
164
- w.quit <- ch
165
- if err := w.wakeupReader(); err != nil {
166
- return err
167
- }
168
- return <-ch
169
-}
170
-
171
-// AddWatch adds path to the watched file set.
172
-func (w *Watcher) AddWatch(path string, flags uint32) error {
173
- if w.isClosed {
174
- return errors.New("watcher already closed")
175
- }
176
- in := &input{
177
- op: opAddWatch,
178
- path: filepath.Clean(path),
179
- flags: flags,
180
- reply: make(chan error),
181
- }
182
- w.input <- in
183
- if err := w.wakeupReader(); err != nil {
184
- return err
185
- }
186
- return <-in.reply
187
-}
188
-
189
-// Watch adds path to the watched file set, watching all events.
190
-func (w *Watcher) watch(path string) error {
191
- return w.AddWatch(path, sys_FS_ALL_EVENTS)
192
-}
193
-
194
-// RemoveWatch removes path from the watched file set.
195
-func (w *Watcher) removeWatch(path string) error {
196
- in := &input{
197
- op: opRemoveWatch,
198
- path: filepath.Clean(path),
199
- reply: make(chan error),
200
- }
201
- w.input <- in
202
- if err := w.wakeupReader(); err != nil {
203
- return err
204
- }
205
- return <-in.reply
206
-}
207
-
208
-func (w *Watcher) wakeupReader() error {
209
- e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
210
- if e != nil {
211
- return os.NewSyscallError("PostQueuedCompletionStatus", e)
212
- }
213
- return nil
214
-}
215
-
216
-func getDir(pathname string) (dir string, err error) {
217
- attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
218
- if e != nil {
219
- return "", os.NewSyscallError("GetFileAttributes", e)
220
- }
221
- if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
222
- dir = pathname
223
- } else {
224
- dir, _ = filepath.Split(pathname)
225
- dir = filepath.Clean(dir)
226
- }
227
- return
228
-}
229
-
230
-func getIno(path string) (ino *inode, err error) {
231
- h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
232
- syscall.FILE_LIST_DIRECTORY,
233
- syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
234
- nil, syscall.OPEN_EXISTING,
235
- syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
236
- if e != nil {
237
- return nil, os.NewSyscallError("CreateFile", e)
238
- }
239
- var fi syscall.ByHandleFileInformation
240
- if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
241
- syscall.CloseHandle(h)
242
- return nil, os.NewSyscallError("GetFileInformationByHandle", e)
243
- }
244
- ino = &inode{
245
- handle: h,
246
- volume: fi.VolumeSerialNumber,
247
- index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
248
- }
249
- return ino, nil
250
-}
251
-
252
-// Must run within the I/O thread.
253
-func (m watchMap) get(ino *inode) *watch {
254
- if i := m[ino.volume]; i != nil {
255
- return i[ino.index]
256
- }
257
- return nil
258
-}
259
-
260
-// Must run within the I/O thread.
261
-func (m watchMap) set(ino *inode, watch *watch) {
262
- i := m[ino.volume]
263
- if i == nil {
264
- i = make(indexMap)
265
- m[ino.volume] = i
266
- }
267
- i[ino.index] = watch
268
-}
269
-
270
-// Must run within the I/O thread.
271
-func (w *Watcher) addWatch(pathname string, flags uint64) error {
272
- dir, err := getDir(pathname)
273
- if err != nil {
274
- return err
275
- }
276
- if flags&sys_FS_ONLYDIR != 0 && pathname != dir {
277
- return nil
278
- }
279
- ino, err := getIno(dir)
280
- if err != nil {
281
- return err
282
- }
283
- w.mu.Lock()
284
- watchEntry := w.watches.get(ino)
285
- w.mu.Unlock()
286
- if watchEntry == nil {
287
- if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
288
- syscall.CloseHandle(ino.handle)
289
- return os.NewSyscallError("CreateIoCompletionPort", e)
290
- }
291
- watchEntry = &watch{
292
- ino: ino,
293
- path: dir,
294
- names: make(map[string]uint64),
295
- }
296
- w.mu.Lock()
297
- w.watches.set(ino, watchEntry)
298
- w.mu.Unlock()
299
- flags |= provisional
300
- } else {
301
- syscall.CloseHandle(ino.handle)
302
- }
303
- if pathname == dir {
304
- watchEntry.mask |= flags
305
- } else {
306
- watchEntry.names[filepath.Base(pathname)] |= flags
307
- }
308
- if err = w.startRead(watchEntry); err != nil {
309
- return err
310
- }
311
- if pathname == dir {
312
- watchEntry.mask &= ^provisional
313
- } else {
314
- watchEntry.names[filepath.Base(pathname)] &= ^provisional
315
- }
316
- return nil
317
-}
318
-
319
-// Must run within the I/O thread.
320
-func (w *Watcher) remWatch(pathname string) error {
321
- dir, err := getDir(pathname)
322
- if err != nil {
323
- return err
324
- }
325
- ino, err := getIno(dir)
326
- if err != nil {
327
- return err
328
- }
329
- w.mu.Lock()
330
- watch := w.watches.get(ino)
331
- w.mu.Unlock()
332
- if watch == nil {
333
- return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
334
- }
335
- if pathname == dir {
336
- w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
337
- watch.mask = 0
338
- } else {
339
- name := filepath.Base(pathname)
340
- w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED)
341
- delete(watch.names, name)
342
- }
343
- return w.startRead(watch)
344
-}
345
-
346
-// Must run within the I/O thread.
347
-func (w *Watcher) deleteWatch(watch *watch) {
348
- for name, mask := range watch.names {
349
- if mask&provisional == 0 {
350
- w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED)
351
- }
352
- delete(watch.names, name)
353
- }
354
- if watch.mask != 0 {
355
- if watch.mask&provisional == 0 {
356
- w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED)
357
- }
358
- watch.mask = 0
359
- }
360
-}
361
-
362
-// Must run within the I/O thread.
363
-func (w *Watcher) startRead(watch *watch) error {
364
- if e := syscall.CancelIo(watch.ino.handle); e != nil {
365
- w.Error <- os.NewSyscallError("CancelIo", e)
366
- w.deleteWatch(watch)
367
- }
368
- mask := toWindowsFlags(watch.mask)
369
- for _, m := range watch.names {
370
- mask |= toWindowsFlags(m)
371
- }
372
- if mask == 0 {
373
- if e := syscall.CloseHandle(watch.ino.handle); e != nil {
374
- w.Error <- os.NewSyscallError("CloseHandle", e)
375
- }
376
- w.mu.Lock()
377
- delete(w.watches[watch.ino.volume], watch.ino.index)
378
- w.mu.Unlock()
379
- return nil
380
- }
381
- e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
382
- uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
383
- if e != nil {
384
- err := os.NewSyscallError("ReadDirectoryChanges", e)
385
- if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
386
- // Watched directory was probably removed
387
- if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) {
388
- if watch.mask&sys_FS_ONESHOT != 0 {
389
- watch.mask = 0
390
- }
391
- }
392
- err = nil
393
- }
394
- w.deleteWatch(watch)
395
- w.startRead(watch)
396
- return err
397
- }
398
- return nil
399
-}
400
-
401
-// readEvents reads from the I/O completion port, converts the
402
-// received events into Event objects and sends them via the Event channel.
403
-// Entry point to the I/O thread.
404
-func (w *Watcher) readEvents() {
405
- var (
406
- n, key uint32
407
- ov *syscall.Overlapped
408
- )
409
- runtime.LockOSThread()
410
-
411
- for {
412
- e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
413
- watch := (*watch)(unsafe.Pointer(ov))
414
-
415
- if watch == nil {
416
- select {
417
- case ch := <-w.quit:
418
- w.mu.Lock()
419
- var indexes []indexMap
420
- for _, index := range w.watches {
421
- indexes = append(indexes, index)
422
- }
423
- w.mu.Unlock()
424
- for _, index := range indexes {
425
- for _, watch := range index {
426
- w.deleteWatch(watch)
427
- w.startRead(watch)
428
- }
429
- }
430
- var err error
431
- if e := syscall.CloseHandle(w.port); e != nil {
432
- err = os.NewSyscallError("CloseHandle", e)
433
- }
434
- close(w.internalEvent)
435
- close(w.Error)
436
- ch <- err
437
- return
438
- case in := <-w.input:
439
- switch in.op {
440
- case opAddWatch:
441
- in.reply <- w.addWatch(in.path, uint64(in.flags))
442
- case opRemoveWatch:
443
- in.reply <- w.remWatch(in.path)
444
- }
445
- default:
446
- }
447
- continue
448
- }
449
-
450
- switch e {
451
- case sys_ERROR_MORE_DATA:
452
- if watch == nil {
453
- w.Error <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
454
- } else {
455
- // The i/o succeeded but the buffer is full.
456
- // In theory we should be building up a full packet.
457
- // In practice we can get away with just carrying on.
458
- n = uint32(unsafe.Sizeof(watch.buf))
459
- }
460
- case syscall.ERROR_ACCESS_DENIED:
461
- // Watched directory was probably removed
462
- w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF)
463
- w.deleteWatch(watch)
464
- w.startRead(watch)
465
- continue
466
- case syscall.ERROR_OPERATION_ABORTED:
467
- // CancelIo was called on this handle
468
- continue
469
- default:
470
- w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
471
- continue
472
- case nil:
473
- }
474
-
475
- var offset uint32
476
- for {
477
- if n == 0 {
478
- w.internalEvent <- &FileEvent{mask: sys_FS_Q_OVERFLOW}
479
- w.Error <- errors.New("short read in readEvents()")
480
- break
481
- }
482
-
483
- // Point "raw" to the event in the buffer
484
- raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
485
- buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
486
- name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
487
- fullname := watch.path + "\\" + name
488
-
489
- var mask uint64
490
- switch raw.Action {
491
- case syscall.FILE_ACTION_REMOVED:
492
- mask = sys_FS_DELETE_SELF
493
- case syscall.FILE_ACTION_MODIFIED:
494
- mask = sys_FS_MODIFY
495
- case syscall.FILE_ACTION_RENAMED_OLD_NAME:
496
- watch.rename = name
497
- case syscall.FILE_ACTION_RENAMED_NEW_NAME:
498
- if watch.names[watch.rename] != 0 {
499
- watch.names[name] |= watch.names[watch.rename]
500
- delete(watch.names, watch.rename)
501
- mask = sys_FS_MOVE_SELF
502
- }
503
- }
504
-
505
- sendNameEvent := func() {
506
- if w.sendEvent(fullname, watch.names[name]&mask) {
507
- if watch.names[name]&sys_FS_ONESHOT != 0 {
508
- delete(watch.names, name)
509
- }
510
- }
511
- }
512
- if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
513
- sendNameEvent()
514
- }
515
- if raw.Action == syscall.FILE_ACTION_REMOVED {
516
- w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED)
517
- delete(watch.names, name)
518
- }
519
- if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
520
- if watch.mask&sys_FS_ONESHOT != 0 {
521
- watch.mask = 0
522
- }
523
- }
524
- if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
525
- fullname = watch.path + "\\" + watch.rename
526
- sendNameEvent()
527
- }
528
-
529
- // Move to the next event in the buffer
530
- if raw.NextEntryOffset == 0 {
531
- break
532
- }
533
- offset += raw.NextEntryOffset
534
-
535
- // Error!
536
- if offset >= n {
537
- w.Error <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
538
- break
539
- }
540
- }
541
-
542
- if err := w.startRead(watch); err != nil {
543
- w.Error <- err
544
- }
545
- }
546
-}
547
-
548
-func (w *Watcher) sendEvent(name string, mask uint64) bool {
549
- if mask == 0 {
550
- return false
551
- }
552
- event := &FileEvent{mask: uint32(mask), Name: name}
553
- if mask&sys_FS_MOVE != 0 {
554
- if mask&sys_FS_MOVED_FROM != 0 {
555
- w.cookie++
556
- }
557
- event.cookie = w.cookie
558
- }
559
- select {
560
- case ch := <-w.quit:
561
- w.quit <- ch
562
- case w.Event <- event:
563
- }
564
- return true
565
-}
566
-
567
-func toWindowsFlags(mask uint64) uint32 {
568
- var m uint32
569
- if mask&sys_FS_ACCESS != 0 {
570
- m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
571
- }
572
- if mask&sys_FS_MODIFY != 0 {
573
- m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
574
- }
575
- if mask&sys_FS_ATTRIB != 0 {
576
- m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
577
- }
578
- if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 {
579
- m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
580
- }
581
- return m
582
-}
583
-
584
-func toFSnotifyFlags(action uint32) uint64 {
585
- switch action {
586
- case syscall.FILE_ACTION_ADDED:
587
- return sys_FS_CREATE
588
- case syscall.FILE_ACTION_REMOVED:
589
- return sys_FS_DELETE
590
- case syscall.FILE_ACTION_MODIFIED:
591
- return sys_FS_MODIFY
592
- case syscall.FILE_ACTION_RENAMED_OLD_NAME:
593
- return sys_FS_MOVED_FROM
594
- case syscall.FILE_ACTION_RENAMED_NEW_NAME:
595
- return sys_FS_MOVED_TO
596
- }
597
- return 0
598
-}
Godeps/_workspace/src/gopkg.in/tomb.v1/LICENSE
deleted
-29
@@ -1,29 +0,0 @@
1
-tomb - support for clean goroutine termination in Go.
2
-
3
-Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
4
-
5
-All rights reserved.
6
-
7
-Redistribution and use in source and binary forms, with or without
8
-modification, are permitted provided that the following conditions are met:
9
-
10
- * Redistributions of source code must retain the above copyright notice,
11
- this list of conditions and the following disclaimer.
12
- * Redistributions in binary form must reproduce the above copyright notice,
13
- this list of conditions and the following disclaimer in the documentation
14
- and/or other materials provided with the distribution.
15
- * Neither the name of the copyright holder nor the names of its
16
- contributors may be used to endorse or promote products derived from
17
- this software without specific prior written permission.
18
-
19
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/gopkg.in/tomb.v1/README.md
deleted
-4
@@ -1,4 +0,0 @@
1
-Installation and usage
2
-----------------------
3
-
4
-See [gopkg.in/tomb.v1](https://gopkg.in/tomb.v1) for documentation and usage details.
Godeps/_workspace/src/gopkg.in/tomb.v1/tomb.go
deleted
-176
@@ -1,176 +0,0 @@
1
-// Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
2
-//
3
-// All rights reserved.
4
-//
5
-// Redistribution and use in source and binary forms, with or without
6
-// modification, are permitted provided that the following conditions are met:
7
-//
8
-// * Redistributions of source code must retain the above copyright notice,
9
-// this list of conditions and the following disclaimer.
10
-// * Redistributions in binary form must reproduce the above copyright notice,
11
-// this list of conditions and the following disclaimer in the documentation
12
-// and/or other materials provided with the distribution.
13
-// * Neither the name of the copyright holder nor the names of its
14
-// contributors may be used to endorse or promote products derived from
15
-// this software without specific prior written permission.
16
-//
17
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
-// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
-// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
-// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
-// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
-// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
-
29
-// The tomb package offers a conventional API for clean goroutine termination.
30
-//
31
-// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
32
-// and the reason for its death.
33
-//
34
-// The zero value of a Tomb assumes that a goroutine is about to be
35
-// created or already alive. Once Kill or Killf is called with an
36
-// argument that informs the reason for death, the goroutine is in
37
-// a dying state and is expected to terminate soon. Right before the
38
-// goroutine function or method returns, Done must be called to inform
39
-// that the goroutine is indeed dead and about to stop running.
40
-//
41
-// A Tomb exposes Dying and Dead channels. These channels are closed
42
-// when the Tomb state changes in the respective way. They enable
43
-// explicit blocking until the state changes, and also to selectively
44
-// unblock select statements accordingly.
45
-//
46
-// When the tomb state changes to dying and there's still logic going
47
-// on within the goroutine, nested functions and methods may choose to
48
-// return ErrDying as their error value, as this error won't alter the
49
-// tomb state if provided to the Kill method. This is a convenient way to
50
-// follow standard Go practices in the context of a dying tomb.
51
-//
52
-// For background and a detailed example, see the following blog post:
53
-//
54
-// http://blog.labix.org/2011/10/09/death-of-goroutines-under-control
55
-//
56
-// For a more complex code snippet demonstrating the use of multiple
57
-// goroutines with a single Tomb, see:
58
-//
59
-// http://play.golang.org/p/Xh7qWsDPZP
60
-//
61
-package tomb
62
-
63
-import (
64
- "errors"
65
- "fmt"
66
- "sync"
67
-)
68
-
69
-// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
70
-// and the reason for its death.
71
-//
72
-// See the package documentation for details.
73
-type Tomb struct {
74
- m sync.Mutex
75
- dying chan struct{}
76
- dead chan struct{}
77
- reason error
78
-}
79
-
80
-var (
81
- ErrStillAlive = errors.New("tomb: still alive")
82
- ErrDying = errors.New("tomb: dying")
83
-)
84
-
85
-func (t *Tomb) init() {
86
- t.m.Lock()
87
- if t.dead == nil {
88
- t.dead = make(chan struct{})
89
- t.dying = make(chan struct{})
90
- t.reason = ErrStillAlive
91
- }
92
- t.m.Unlock()
93
-}
94
-
95
-// Dead returns the channel that can be used to wait
96
-// until t.Done has been called.
97
-func (t *Tomb) Dead() <-chan struct{} {
98
- t.init()
99
- return t.dead
100
-}
101
-
102
-// Dying returns the channel that can be used to wait
103
-// until t.Kill or t.Done has been called.
104
-func (t *Tomb) Dying() <-chan struct{} {
105
- t.init()
106
- return t.dying
107
-}
108
-
109
-// Wait blocks until the goroutine is in a dead state and returns the
110
-// reason for its death.
111
-func (t *Tomb) Wait() error {
112
- t.init()
113
- <-t.dead
114
- t.m.Lock()
115
- reason := t.reason
116
- t.m.Unlock()
117
- return reason
118
-}
119
-
120
-// Done flags the goroutine as dead, and should be called a single time
121
-// right before the goroutine function or method returns.
122
-// If the goroutine was not already in a dying state before Done is
123
-// called, it will be flagged as dying and dead at once with no
124
-// error.
125
-func (t *Tomb) Done() {
126
- t.Kill(nil)
127
- close(t.dead)
128
-}
129
-
130
-// Kill flags the goroutine as dying for the given reason.
131
-// Kill may be called multiple times, but only the first
132
-// non-nil error is recorded as the reason for termination.
133
-//
134
-// If reason is ErrDying, the previous reason isn't replaced
135
-// even if it is nil. It's a runtime error to call Kill with
136
-// ErrDying if t is not in a dying state.
137
-func (t *Tomb) Kill(reason error) {
138
- t.init()
139
- t.m.Lock()
140
- defer t.m.Unlock()
141
- if reason == ErrDying {
142
- if t.reason == ErrStillAlive {
143
- panic("tomb: Kill with ErrDying while still alive")
144
- }
145
- return
146
- }
147
- if t.reason == nil || t.reason == ErrStillAlive {
148
- t.reason = reason
149
- }
150
- // If the receive on t.dying succeeds, then
151
- // it can only be because we have already closed it.
152
- // If it blocks, then we know that it needs to be closed.
153
- select {
154
- case <-t.dying:
155
- default:
156
- close(t.dying)
157
- }
158
-}
159
-
160
-// Killf works like Kill, but builds the reason providing the received
161
-// arguments to fmt.Errorf. The generated error is also returned.
162
-func (t *Tomb) Killf(f string, a ...interface{}) error {
163
- err := fmt.Errorf(f, a...)
164
- t.Kill(err)
165
- return err
166
-}
167
-
168
-// Err returns the reason for the goroutine death provided via Kill
169
-// or Killf, or ErrStillAlive when the goroutine is still alive.
170
-func (t *Tomb) Err() (reason error) {
171
- t.init()
172
- t.m.Lock()
173
- reason = t.reason
174
- t.m.Unlock()
175
- return
176
-}
Godeps/_workspace/src/gopkg.in/tomb.v1/tomb_test.go
deleted
-114
@@ -1,114 +0,0 @@
1
-package tomb_test
2
-
3
-import (
4
- "errors"
5
- "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/tomb.v1"
6
- "reflect"
7
- "testing"
8
-)
9
-
10
-func TestNewTomb(t *testing.T) {
11
- tb := &tomb.Tomb{}
12
- testState(t, tb, false, false, tomb.ErrStillAlive)
13
-
14
- tb.Done()
15
- testState(t, tb, true, true, nil)
16
-}
17
-
18
-func TestKill(t *testing.T) {
19
- // a nil reason flags the goroutine as dying
20
- tb := &tomb.Tomb{}
21
- tb.Kill(nil)
22
- testState(t, tb, true, false, nil)
23
-
24
- // a non-nil reason now will override Kill
25
- err := errors.New("some error")
26
- tb.Kill(err)
27
- testState(t, tb, true, false, err)
28
-
29
- // another non-nil reason won't replace the first one
30
- tb.Kill(errors.New("ignore me"))
31
- testState(t, tb, true, false, err)
32
-
33
- tb.Done()
34
- testState(t, tb, true, true, err)
35
-}
36
-
37
-func TestKillf(t *testing.T) {
38
- tb := &tomb.Tomb{}
39
-
40
- err := tb.Killf("BO%s", "OM")
41
- if s := err.Error(); s != "BOOM" {
42
- t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
43
- }
44
- testState(t, tb, true, false, err)
45
-
46
- // another non-nil reason won't replace the first one
47
- tb.Killf("ignore me")
48
- testState(t, tb, true, false, err)
49
-
50
- tb.Done()
51
- testState(t, tb, true, true, err)
52
-}
53
-
54
-func TestErrDying(t *testing.T) {
55
- // ErrDying being used properly, after a clean death.
56
- tb := &tomb.Tomb{}
57
- tb.Kill(nil)
58
- tb.Kill(tomb.ErrDying)
59
- testState(t, tb, true, false, nil)
60
-
61
- // ErrDying being used properly, after an errorful death.
62
- err := errors.New("some error")
63
- tb.Kill(err)
64
- tb.Kill(tomb.ErrDying)
65
- testState(t, tb, true, false, err)
66
-
67
- // ErrDying being used badly, with an alive tomb.
68
- tb = &tomb.Tomb{}
69
- defer func() {
70
- err := recover()
71
- if err != "tomb: Kill with ErrDying while still alive" {
72
- t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
73
- }
74
- testState(t, tb, false, false, tomb.ErrStillAlive)
75
- }()
76
- tb.Kill(tomb.ErrDying)
77
-}
78
-
79
-func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
80
- select {
81
- case <-tb.Dying():
82
- if !wantDying {
83
- t.Error("<-Dying: should block")
84
- }
85
- default:
86
- if wantDying {
87
- t.Error("<-Dying: should not block")
88
- }
89
- }
90
- seemsDead := false
91
- select {
92
- case <-tb.Dead():
93
- if !wantDead {
94
- t.Error("<-Dead: should block")
95
- }
96
- seemsDead = true
97
- default:
98
- if wantDead {
99
- t.Error("<-Dead: should not block")
100
- }
101
- }
102
- if err := tb.Err(); err != wantErr {
103
- t.Errorf("Err: want %#v, got %#v", wantErr, err)
104
- }
105
- if wantDead && seemsDead {
106
- waitErr := tb.Wait()
107
- switch {
108
- case waitErr == tomb.ErrStillAlive:
109
- t.Errorf("Wait should not return ErrStillAlive")
110
- case !reflect.DeepEqual(waitErr, wantErr):
111
- t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
112
- }
113
- }
114
-}