From fa9658cbfaa94fd5d0425041404e498c01db1aa3 Mon Sep 17 00:00:00 2001 From: baude Date: Tue, 6 Feb 2018 09:07:51 -0600 Subject: podman logs: fix tailing Fix issues with tailing of container logs as described in issue #16. Also add in the ability to use a duration or known time stamp formats for the --since flag. Signed-off-by: baude Closes: #317 Approved by: mheon --- vendor/github.com/hpcloud/tail/LICENSE.txt | 21 - vendor/github.com/hpcloud/tail/README.md | 28 -- .../hpcloud/tail/ratelimiter/leakybucket.go | 97 ----- .../github.com/hpcloud/tail/ratelimiter/memory.go | 58 --- .../github.com/hpcloud/tail/ratelimiter/storage.go | 6 - vendor/github.com/hpcloud/tail/tail.go | 438 --------------------- vendor/github.com/hpcloud/tail/tail_posix.go | 11 - vendor/github.com/hpcloud/tail/tail_windows.go | 12 - vendor/github.com/hpcloud/tail/util/util.go | 48 --- .../github.com/hpcloud/tail/watch/filechanges.go | 36 -- vendor/github.com/hpcloud/tail/watch/inotify.go | 128 ------ .../hpcloud/tail/watch/inotify_tracker.go | 260 ------------ vendor/github.com/hpcloud/tail/watch/polling.go | 118 ------ vendor/github.com/hpcloud/tail/watch/watch.go | 20 - vendor/github.com/hpcloud/tail/winfile/winfile.go | 92 ----- 15 files changed, 1373 deletions(-) delete mode 100644 vendor/github.com/hpcloud/tail/LICENSE.txt delete mode 100644 vendor/github.com/hpcloud/tail/README.md delete mode 100644 vendor/github.com/hpcloud/tail/ratelimiter/leakybucket.go delete mode 100644 vendor/github.com/hpcloud/tail/ratelimiter/memory.go delete mode 100644 vendor/github.com/hpcloud/tail/ratelimiter/storage.go delete mode 100644 vendor/github.com/hpcloud/tail/tail.go delete mode 100644 vendor/github.com/hpcloud/tail/tail_posix.go delete mode 100644 vendor/github.com/hpcloud/tail/tail_windows.go delete mode 100644 vendor/github.com/hpcloud/tail/util/util.go delete mode 100644 vendor/github.com/hpcloud/tail/watch/filechanges.go delete mode 100644 vendor/github.com/hpcloud/tail/watch/inotify.go delete mode 100644 vendor/github.com/hpcloud/tail/watch/inotify_tracker.go delete mode 100644 vendor/github.com/hpcloud/tail/watch/polling.go delete mode 100644 vendor/github.com/hpcloud/tail/watch/watch.go delete mode 100644 vendor/github.com/hpcloud/tail/winfile/winfile.go (limited to 'vendor/github.com/hpcloud') diff --git a/vendor/github.com/hpcloud/tail/LICENSE.txt b/vendor/github.com/hpcloud/tail/LICENSE.txt deleted file mode 100644 index 818d802a5..000000000 --- a/vendor/github.com/hpcloud/tail/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -# The MIT License (MIT) - -# © Copyright 2015 Hewlett Packard Enterprise Development LP -Copyright (c) 2014 ActiveState - -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: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. diff --git a/vendor/github.com/hpcloud/tail/README.md b/vendor/github.com/hpcloud/tail/README.md deleted file mode 100644 index fb7fbc26c..000000000 --- a/vendor/github.com/hpcloud/tail/README.md +++ /dev/null @@ -1,28 +0,0 @@ -[![Build Status](https://travis-ci.org/hpcloud/tail.svg)](https://travis-ci.org/hpcloud/tail) -[![Build status](https://ci.appveyor.com/api/projects/status/kohpsf3rvhjhrox6?svg=true)](https://ci.appveyor.com/project/HelionCloudFoundry/tail) - -# Go package for tail-ing files - -A Go package striving to emulate the features of the BSD `tail` program. - -```Go -t, err := tail.TailFile("/var/log/nginx.log", tail.Config{Follow: true}) -for line := range t.Lines { - fmt.Println(line.Text) -} -``` - -See [API documentation](http://godoc.org/github.com/hpcloud/tail). - -## Log rotation - -Tail comes with full support for truncation/move detection as it is -designed to work with log rotation tools. - -## Installing - - go get github.com/hpcloud/tail/... - -## Windows support - -This package [needs assistance](https://github.com/hpcloud/tail/labels/Windows) for full Windows support. diff --git a/vendor/github.com/hpcloud/tail/ratelimiter/leakybucket.go b/vendor/github.com/hpcloud/tail/ratelimiter/leakybucket.go deleted file mode 100644 index 358b69e7f..000000000 --- a/vendor/github.com/hpcloud/tail/ratelimiter/leakybucket.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package ratelimiter implements the Leaky Bucket ratelimiting algorithm with memcached and in-memory backends. -package ratelimiter - -import ( - "time" -) - -type LeakyBucket struct { - Size uint16 - Fill float64 - LeakInterval time.Duration // time.Duration for 1 unit of size to leak - Lastupdate time.Time - Now func() time.Time -} - -func NewLeakyBucket(size uint16, leakInterval time.Duration) *LeakyBucket { - bucket := LeakyBucket{ - Size: size, - Fill: 0, - LeakInterval: leakInterval, - Now: time.Now, - Lastupdate: time.Now(), - } - - return &bucket -} - -func (b *LeakyBucket) updateFill() { - now := b.Now() - if b.Fill > 0 { - elapsed := now.Sub(b.Lastupdate) - - b.Fill -= float64(elapsed) / float64(b.LeakInterval) - if b.Fill < 0 { - b.Fill = 0 - } - } - b.Lastupdate = now -} - -func (b *LeakyBucket) Pour(amount uint16) bool { - b.updateFill() - - var newfill float64 = b.Fill + float64(amount) - - if newfill > float64(b.Size) { - return false - } - - b.Fill = newfill - - return true -} - -// The time at which this bucket will be completely drained -func (b *LeakyBucket) DrainedAt() time.Time { - return b.Lastupdate.Add(time.Duration(b.Fill * float64(b.LeakInterval))) -} - -// The duration until this bucket is completely drained -func (b *LeakyBucket) TimeToDrain() time.Duration { - return b.DrainedAt().Sub(b.Now()) -} - -func (b *LeakyBucket) TimeSinceLastUpdate() time.Duration { - return b.Now().Sub(b.Lastupdate) -} - -type LeakyBucketSer struct { - Size uint16 - Fill float64 - LeakInterval time.Duration // time.Duration for 1 unit of size to leak - Lastupdate time.Time -} - -func (b *LeakyBucket) Serialise() *LeakyBucketSer { - bucket := LeakyBucketSer{ - Size: b.Size, - Fill: b.Fill, - LeakInterval: b.LeakInterval, - Lastupdate: b.Lastupdate, - } - - return &bucket -} - -func (b *LeakyBucketSer) DeSerialise() *LeakyBucket { - bucket := LeakyBucket{ - Size: b.Size, - Fill: b.Fill, - LeakInterval: b.LeakInterval, - Lastupdate: b.Lastupdate, - Now: time.Now, - } - - return &bucket -} diff --git a/vendor/github.com/hpcloud/tail/ratelimiter/memory.go b/vendor/github.com/hpcloud/tail/ratelimiter/memory.go deleted file mode 100644 index 8f6a5784a..000000000 --- a/vendor/github.com/hpcloud/tail/ratelimiter/memory.go +++ /dev/null @@ -1,58 +0,0 @@ -package ratelimiter - -import ( - "errors" - "time" -) - -const GC_SIZE int = 100 - -type Memory struct { - store map[string]LeakyBucket - lastGCCollected time.Time -} - -func NewMemory() *Memory { - m := new(Memory) - m.store = make(map[string]LeakyBucket) - m.lastGCCollected = time.Now() - return m -} - -func (m *Memory) GetBucketFor(key string) (*LeakyBucket, error) { - - bucket, ok := m.store[key] - if !ok { - return nil, errors.New("miss") - } - - return &bucket, nil -} - -func (m *Memory) SetBucketFor(key string, bucket LeakyBucket) error { - - if len(m.store) > GC_SIZE { - m.GarbageCollect() - } - - m.store[key] = bucket - - return nil -} - -func (m *Memory) GarbageCollect() { - now := time.Now() - - // rate limit GC to once per minute - if now.Add(60*time.Second).Unix() > m.lastGCCollected.Unix() { - - for key, bucket := range m.store { - // if the bucket is drained, then GC - if bucket.DrainedAt().Unix() > now.Unix() { - delete(m.store, key) - } - } - - m.lastGCCollected = now - } -} diff --git a/vendor/github.com/hpcloud/tail/ratelimiter/storage.go b/vendor/github.com/hpcloud/tail/ratelimiter/storage.go deleted file mode 100644 index 89b2fe882..000000000 --- a/vendor/github.com/hpcloud/tail/ratelimiter/storage.go +++ /dev/null @@ -1,6 +0,0 @@ -package ratelimiter - -type Storage interface { - GetBucketFor(string) (*LeakyBucket, error) - SetBucketFor(string, LeakyBucket) error -} diff --git a/vendor/github.com/hpcloud/tail/tail.go b/vendor/github.com/hpcloud/tail/tail.go deleted file mode 100644 index 2d252d605..000000000 --- a/vendor/github.com/hpcloud/tail/tail.go +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package tail - -import ( - "bufio" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "strings" - "sync" - "time" - - "github.com/hpcloud/tail/ratelimiter" - "github.com/hpcloud/tail/util" - "github.com/hpcloud/tail/watch" - "gopkg.in/tomb.v1" -) - -var ( - ErrStop = fmt.Errorf("tail should now stop") -) - -type Line struct { - Text string - Time time.Time - Err error // Error from tail -} - -// NewLine returns a Line with present time. -func NewLine(text string) *Line { - return &Line{text, time.Now(), nil} -} - -// SeekInfo represents arguments to `os.Seek` -type SeekInfo struct { - Offset int64 - Whence int // os.SEEK_* -} - -type logger interface { - Fatal(v ...interface{}) - Fatalf(format string, v ...interface{}) - Fatalln(v ...interface{}) - Panic(v ...interface{}) - Panicf(format string, v ...interface{}) - Panicln(v ...interface{}) - Print(v ...interface{}) - Printf(format string, v ...interface{}) - Println(v ...interface{}) -} - -// Config is used to specify how a file must be tailed. -type Config struct { - // File-specifc - Location *SeekInfo // Seek to this location before tailing - ReOpen bool // Reopen recreated files (tail -F) - MustExist bool // Fail early if the file does not exist - Poll bool // Poll for file changes instead of using inotify - Pipe bool // Is a named pipe (mkfifo) - RateLimiter *ratelimiter.LeakyBucket - - // Generic IO - Follow bool // Continue looking for new lines (tail -f) - MaxLineSize int // If non-zero, split longer lines into multiple lines - - // Logger, when nil, is set to tail.DefaultLogger - // To disable logging: set field to tail.DiscardingLogger - Logger logger -} - -type Tail struct { - Filename string - Lines chan *Line - Config - - file *os.File - reader *bufio.Reader - - watcher watch.FileWatcher - changes *watch.FileChanges - - tomb.Tomb // provides: Done, Kill, Dying - - lk sync.Mutex -} - -var ( - // DefaultLogger is used when Config.Logger == nil - DefaultLogger = log.New(os.Stderr, "", log.LstdFlags) - // DiscardingLogger can be used to disable logging output - DiscardingLogger = log.New(ioutil.Discard, "", 0) -) - -// TailFile begins tailing the file. Output stream is made available -// via the `Tail.Lines` channel. To handle errors during tailing, -// invoke the `Wait` or `Err` method after finishing reading from the -// `Lines` channel. -func TailFile(filename string, config Config) (*Tail, error) { - if config.ReOpen && !config.Follow { - util.Fatal("cannot set ReOpen without Follow.") - } - - t := &Tail{ - Filename: filename, - Lines: make(chan *Line), - Config: config, - } - - // when Logger was not specified in config, use default logger - if t.Logger == nil { - t.Logger = log.New(os.Stderr, "", log.LstdFlags) - } - - if t.Poll { - t.watcher = watch.NewPollingFileWatcher(filename) - } else { - t.watcher = watch.NewInotifyFileWatcher(filename) - } - - if t.MustExist { - var err error - t.file, err = OpenFile(t.Filename) - if err != nil { - return nil, err - } - } - - go t.tailFileSync() - - return t, nil -} - -// Return the file's current position, like stdio's ftell(). -// But this value is not very accurate. -// it may readed one line in the chan(tail.Lines), -// so it may lost one line. -func (tail *Tail) Tell() (offset int64, err error) { - if tail.file == nil { - return - } - offset, err = tail.file.Seek(0, os.SEEK_CUR) - if err != nil { - return - } - - tail.lk.Lock() - defer tail.lk.Unlock() - if tail.reader == nil { - return - } - - offset -= int64(tail.reader.Buffered()) - return -} - -// Stop stops the tailing activity. -func (tail *Tail) Stop() error { - tail.Kill(nil) - return tail.Wait() -} - -// StopAtEOF stops tailing as soon as the end of the file is reached. -func (tail *Tail) StopAtEOF() error { - tail.Kill(errStopAtEOF) - return tail.Wait() -} - -var errStopAtEOF = errors.New("tail: stop at eof") - -func (tail *Tail) close() { - close(tail.Lines) - tail.closeFile() -} - -func (tail *Tail) closeFile() { - if tail.file != nil { - tail.file.Close() - tail.file = nil - } -} - -func (tail *Tail) reopen() error { - tail.closeFile() - for { - var err error - tail.file, err = OpenFile(tail.Filename) - if err != nil { - if os.IsNotExist(err) { - tail.Logger.Printf("Waiting for %s to appear...", tail.Filename) - if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil { - if err == tomb.ErrDying { - return err - } - return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err) - } - continue - } - return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err) - } - break - } - return nil -} - -func (tail *Tail) readLine() (string, error) { - tail.lk.Lock() - line, err := tail.reader.ReadString('\n') - tail.lk.Unlock() - if err != nil { - // Note ReadString "returns the data read before the error" in - // case of an error, including EOF, so we return it as is. The - // caller is expected to process it if err is EOF. - return line, err - } - - line = strings.TrimRight(line, "\n") - - return line, err -} - -func (tail *Tail) tailFileSync() { - defer tail.Done() - defer tail.close() - - if !tail.MustExist { - // deferred first open. - err := tail.reopen() - if err != nil { - if err != tomb.ErrDying { - tail.Kill(err) - } - return - } - } - - // Seek to requested location on first open of the file. - if tail.Location != nil { - _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence) - tail.Logger.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location) - if err != nil { - tail.Killf("Seek error on %s: %s", tail.Filename, err) - return - } - } - - tail.openReader() - - var offset int64 = 0 - var err error - - // Read line by line. - for { - // do not seek in named pipes - if !tail.Pipe { - // grab the position in case we need to back up in the event of a half-line - offset, err = tail.Tell() - if err != nil { - tail.Kill(err) - return - } - } - - line, err := tail.readLine() - - // Process `line` even if err is EOF. - if err == nil { - cooloff := !tail.sendLine(line) - if cooloff { - // Wait a second before seeking till the end of - // file when rate limit is reached. - msg := fmt.Sprintf( - "Too much log activity; waiting a second " + - "before resuming tailing") - tail.Lines <- &Line{msg, time.Now(), fmt.Errorf(msg)} - select { - case <-time.After(time.Second): - case <-tail.Dying(): - return - } - if err := tail.seekEnd(); err != nil { - tail.Kill(err) - return - } - } - } else if err == io.EOF { - if !tail.Follow { - if line != "" { - tail.sendLine(line) - } - return - } - - if tail.Follow && line != "" { - // this has the potential to never return the last line if - // it's not followed by a newline; seems a fair trade here - err := tail.seekTo(SeekInfo{Offset: offset, Whence: 0}) - if err != nil { - tail.Kill(err) - return - } - } - - // When EOF is reached, wait for more data to become - // available. Wait strategy is based on the `tail.watcher` - // implementation (inotify or polling). - err := tail.waitForChanges() - if err != nil { - if err != ErrStop { - tail.Kill(err) - } - return - } - } else { - // non-EOF error - tail.Killf("Error reading %s: %s", tail.Filename, err) - return - } - - select { - case <-tail.Dying(): - if tail.Err() == errStopAtEOF { - continue - } - return - default: - } - } -} - -// waitForChanges waits until the file has been appended, deleted, -// moved or truncated. When moved or deleted - the file will be -// reopened if ReOpen is true. Truncated files are always reopened. -func (tail *Tail) waitForChanges() error { - if tail.changes == nil { - pos, err := tail.file.Seek(0, os.SEEK_CUR) - if err != nil { - return err - } - tail.changes, err = tail.watcher.ChangeEvents(&tail.Tomb, pos) - if err != nil { - return err - } - } - - select { - case <-tail.changes.Modified: - return nil - case <-tail.changes.Deleted: - tail.changes = nil - if tail.ReOpen { - // XXX: we must not log from a library. - tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename) - if err := tail.reopen(); err != nil { - return err - } - tail.Logger.Printf("Successfully reopened %s", tail.Filename) - tail.openReader() - return nil - } else { - tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename) - return ErrStop - } - case <-tail.changes.Truncated: - // Always reopen truncated files (Follow is true) - tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename) - if err := tail.reopen(); err != nil { - return err - } - tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename) - tail.openReader() - return nil - case <-tail.Dying(): - return ErrStop - } - panic("unreachable") -} - -func (tail *Tail) openReader() { - if tail.MaxLineSize > 0 { - // add 2 to account for newline characters - tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2) - } else { - tail.reader = bufio.NewReader(tail.file) - } -} - -func (tail *Tail) seekEnd() error { - return tail.seekTo(SeekInfo{Offset: 0, Whence: os.SEEK_END}) -} - -func (tail *Tail) seekTo(pos SeekInfo) error { - _, err := tail.file.Seek(pos.Offset, pos.Whence) - if err != nil { - return fmt.Errorf("Seek error on %s: %s", tail.Filename, err) - } - // Reset the read buffer whenever the file is re-seek'ed - tail.reader.Reset(tail.file) - return nil -} - -// sendLine sends the line(s) to Lines channel, splitting longer lines -// if necessary. Return false if rate limit is reached. -func (tail *Tail) sendLine(line string) bool { - now := time.Now() - lines := []string{line} - - // Split longer lines - if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize { - lines = util.PartitionString(line, tail.MaxLineSize) - } - - for _, line := range lines { - tail.Lines <- &Line{line, now, nil} - } - - if tail.Config.RateLimiter != nil { - ok := tail.Config.RateLimiter.Pour(uint16(len(lines))) - if !ok { - tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.\n", - tail.Filename) - return false - } - } - - return true -} - -// Cleanup removes inotify watches added by the tail package. This function is -// meant to be invoked from a process's exit handler. Linux kernel may not -// automatically remove inotify watches after the process exits. -func (tail *Tail) Cleanup() { - watch.Cleanup(tail.Filename) -} diff --git a/vendor/github.com/hpcloud/tail/tail_posix.go b/vendor/github.com/hpcloud/tail/tail_posix.go deleted file mode 100644 index bc4dc3357..000000000 --- a/vendor/github.com/hpcloud/tail/tail_posix.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build linux darwin freebsd netbsd openbsd - -package tail - -import ( - "os" -) - -func OpenFile(name string) (file *os.File, err error) { - return os.Open(name) -} diff --git a/vendor/github.com/hpcloud/tail/tail_windows.go b/vendor/github.com/hpcloud/tail/tail_windows.go deleted file mode 100644 index ef2cfca1b..000000000 --- a/vendor/github.com/hpcloud/tail/tail_windows.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build windows - -package tail - -import ( - "github.com/hpcloud/tail/winfile" - "os" -) - -func OpenFile(name string) (file *os.File, err error) { - return winfile.OpenFile(name, os.O_RDONLY, 0) -} diff --git a/vendor/github.com/hpcloud/tail/util/util.go b/vendor/github.com/hpcloud/tail/util/util.go deleted file mode 100644 index 54151fe39..000000000 --- a/vendor/github.com/hpcloud/tail/util/util.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package util - -import ( - "fmt" - "log" - "os" - "runtime/debug" -) - -type Logger struct { - *log.Logger -} - -var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)} - -// fatal is like panic except it displays only the current goroutine's stack. -func Fatal(format string, v ...interface{}) { - // https://github.com/hpcloud/log/blob/master/log.go#L45 - LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack())) - os.Exit(1) -} - -// partitionString partitions the string into chunks of given size, -// with the last chunk of variable size. -func PartitionString(s string, chunkSize int) []string { - if chunkSize <= 0 { - panic("invalid chunkSize") - } - length := len(s) - chunks := 1 + length/chunkSize - start := 0 - end := chunkSize - parts := make([]string, 0, chunks) - for { - if end > length { - end = length - } - parts = append(parts, s[start:end]) - if end == length { - break - } - start, end = end, end+chunkSize - } - return parts -} diff --git a/vendor/github.com/hpcloud/tail/watch/filechanges.go b/vendor/github.com/hpcloud/tail/watch/filechanges.go deleted file mode 100644 index 3ce5dcecb..000000000 --- a/vendor/github.com/hpcloud/tail/watch/filechanges.go +++ /dev/null @@ -1,36 +0,0 @@ -package watch - -type FileChanges struct { - Modified chan bool // Channel to get notified of modifications - Truncated chan bool // Channel to get notified of truncations - Deleted chan bool // Channel to get notified of deletions/renames -} - -func NewFileChanges() *FileChanges { - return &FileChanges{ - make(chan bool), make(chan bool), make(chan bool)} -} - -func (fc *FileChanges) NotifyModified() { - sendOnlyIfEmpty(fc.Modified) -} - -func (fc *FileChanges) NotifyTruncated() { - sendOnlyIfEmpty(fc.Truncated) -} - -func (fc *FileChanges) NotifyDeleted() { - sendOnlyIfEmpty(fc.Deleted) -} - -// sendOnlyIfEmpty sends on a bool channel only if the channel has no -// backlog to be read by other goroutines. This concurrency pattern -// can be used to notify other goroutines if and only if they are -// looking for it (i.e., subsequent notifications can be compressed -// into one). -func sendOnlyIfEmpty(ch chan bool) { - select { - case ch <- true: - default: - } -} diff --git a/vendor/github.com/hpcloud/tail/watch/inotify.go b/vendor/github.com/hpcloud/tail/watch/inotify.go deleted file mode 100644 index 4478f1e1a..000000000 --- a/vendor/github.com/hpcloud/tail/watch/inotify.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package watch - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/hpcloud/tail/util" - - "gopkg.in/fsnotify.v1" - "gopkg.in/tomb.v1" -) - -// InotifyFileWatcher uses inotify to monitor file changes. -type InotifyFileWatcher struct { - Filename string - Size int64 -} - -func NewInotifyFileWatcher(filename string) *InotifyFileWatcher { - fw := &InotifyFileWatcher{filepath.Clean(filename), 0} - return fw -} - -func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error { - err := WatchCreate(fw.Filename) - if err != nil { - return err - } - defer RemoveWatchCreate(fw.Filename) - - // Do a real check now as the file might have been created before - // calling `WatchFlags` above. - if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) { - // file exists, or stat returned an error. - return err - } - - events := Events(fw.Filename) - - for { - select { - case evt, ok := <-events: - if !ok { - return fmt.Errorf("inotify watcher has been closed") - } - evtName, err := filepath.Abs(evt.Name) - if err != nil { - return err - } - fwFilename, err := filepath.Abs(fw.Filename) - if err != nil { - return err - } - if evtName == fwFilename { - return nil - } - case <-t.Dying(): - return tomb.ErrDying - } - } - panic("unreachable") -} - -func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { - err := Watch(fw.Filename) - if err != nil { - return nil, err - } - - changes := NewFileChanges() - fw.Size = pos - - go func() { - defer RemoveWatch(fw.Filename) - - events := Events(fw.Filename) - - for { - prevSize := fw.Size - - var evt fsnotify.Event - var ok bool - - select { - case evt, ok = <-events: - if !ok { - return - } - case <-t.Dying(): - return - } - - switch { - case evt.Op&fsnotify.Remove == fsnotify.Remove: - fallthrough - - case evt.Op&fsnotify.Rename == fsnotify.Rename: - changes.NotifyDeleted() - return - - case evt.Op&fsnotify.Write == fsnotify.Write: - fi, err := os.Stat(fw.Filename) - if err != nil { - if os.IsNotExist(err) { - changes.NotifyDeleted() - return - } - // XXX: report this error back to the user - util.Fatal("Failed to stat file %v: %v", fw.Filename, err) - } - fw.Size = fi.Size() - - if prevSize > 0 && prevSize > fw.Size { - changes.NotifyTruncated() - } else { - changes.NotifyModified() - } - prevSize = fw.Size - } - } - }() - - return changes, nil -} diff --git a/vendor/github.com/hpcloud/tail/watch/inotify_tracker.go b/vendor/github.com/hpcloud/tail/watch/inotify_tracker.go deleted file mode 100644 index 03be4275c..000000000 --- a/vendor/github.com/hpcloud/tail/watch/inotify_tracker.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package watch - -import ( - "log" - "os" - "path/filepath" - "sync" - "syscall" - - "github.com/hpcloud/tail/util" - - "gopkg.in/fsnotify.v1" -) - -type InotifyTracker struct { - mux sync.Mutex - watcher *fsnotify.Watcher - chans map[string]chan fsnotify.Event - done map[string]chan bool - watchNums map[string]int - watch chan *watchInfo - remove chan *watchInfo - error chan error -} - -type watchInfo struct { - op fsnotify.Op - fname string -} - -func (this *watchInfo) isCreate() bool { - return this.op == fsnotify.Create -} - -var ( - // globally shared InotifyTracker; ensures only one fsnotify.Watcher is used - shared *InotifyTracker - - // these are used to ensure the shared InotifyTracker is run exactly once - once = sync.Once{} - goRun = func() { - shared = &InotifyTracker{ - mux: sync.Mutex{}, - chans: make(map[string]chan fsnotify.Event), - done: make(map[string]chan bool), - watchNums: make(map[string]int), - watch: make(chan *watchInfo), - remove: make(chan *watchInfo), - error: make(chan error), - } - go shared.run() - } - - logger = log.New(os.Stderr, "", log.LstdFlags) -) - -// Watch signals the run goroutine to begin watching the input filename -func Watch(fname string) error { - return watch(&watchInfo{ - fname: fname, - }) -} - -// Watch create signals the run goroutine to begin watching the input filename -// if call the WatchCreate function, don't call the Cleanup, call the RemoveWatchCreate -func WatchCreate(fname string) error { - return watch(&watchInfo{ - op: fsnotify.Create, - fname: fname, - }) -} - -func watch(winfo *watchInfo) error { - // start running the shared InotifyTracker if not already running - once.Do(goRun) - - winfo.fname = filepath.Clean(winfo.fname) - shared.watch <- winfo - return <-shared.error -} - -// RemoveWatch signals the run goroutine to remove the watch for the input filename -func RemoveWatch(fname string) { - remove(&watchInfo{ - fname: fname, - }) -} - -// RemoveWatch create signals the run goroutine to remove the watch for the input filename -func RemoveWatchCreate(fname string) { - remove(&watchInfo{ - op: fsnotify.Create, - fname: fname, - }) -} - -func remove(winfo *watchInfo) { - // start running the shared InotifyTracker if not already running - once.Do(goRun) - - winfo.fname = filepath.Clean(winfo.fname) - shared.mux.Lock() - done := shared.done[winfo.fname] - if done != nil { - delete(shared.done, winfo.fname) - close(done) - } - - fname := winfo.fname - if winfo.isCreate() { - // Watch for new files to be created in the parent directory. - fname = filepath.Dir(fname) - } - shared.watchNums[fname]-- - watchNum := shared.watchNums[fname] - if watchNum == 0 { - delete(shared.watchNums, fname) - } - shared.mux.Unlock() - - // If we were the last ones to watch this file, unsubscribe from inotify. - // This needs to happen after releasing the lock because fsnotify waits - // synchronously for the kernel to acknowledge the removal of the watch - // for this file, which causes us to deadlock if we still held the lock. - if watchNum == 0 { - shared.watcher.Remove(fname) - } - shared.remove <- winfo -} - -// Events returns a channel to which FileEvents corresponding to the input filename -// will be sent. This channel will be closed when removeWatch is called on this -// filename. -func Events(fname string) <-chan fsnotify.Event { - shared.mux.Lock() - defer shared.mux.Unlock() - - return shared.chans[fname] -} - -// Cleanup removes the watch for the input filename if necessary. -func Cleanup(fname string) { - RemoveWatch(fname) -} - -// watchFlags calls fsnotify.WatchFlags for the input filename and flags, creating -// a new Watcher if the previous Watcher was closed. -func (shared *InotifyTracker) addWatch(winfo *watchInfo) error { - shared.mux.Lock() - defer shared.mux.Unlock() - - if shared.chans[winfo.fname] == nil { - shared.chans[winfo.fname] = make(chan fsnotify.Event) - shared.done[winfo.fname] = make(chan bool) - } - - fname := winfo.fname - if winfo.isCreate() { - // Watch for new files to be created in the parent directory. - fname = filepath.Dir(fname) - } - - // already in inotify watch - if shared.watchNums[fname] > 0 { - shared.watchNums[fname]++ - if winfo.isCreate() { - shared.watchNums[winfo.fname]++ - } - return nil - } - - err := shared.watcher.Add(fname) - if err == nil { - shared.watchNums[fname]++ - if winfo.isCreate() { - shared.watchNums[winfo.fname]++ - } - } - return err -} - -// removeWatch calls fsnotify.RemoveWatch for the input filename and closes the -// corresponding events channel. -func (shared *InotifyTracker) removeWatch(winfo *watchInfo) { - shared.mux.Lock() - defer shared.mux.Unlock() - - ch := shared.chans[winfo.fname] - if ch == nil { - return - } - - delete(shared.chans, winfo.fname) - close(ch) - - if !winfo.isCreate() { - return - } - - shared.watchNums[winfo.fname]-- - if shared.watchNums[winfo.fname] == 0 { - delete(shared.watchNums, winfo.fname) - } -} - -// sendEvent sends the input event to the appropriate Tail. -func (shared *InotifyTracker) sendEvent(event fsnotify.Event) { - name := filepath.Clean(event.Name) - - shared.mux.Lock() - ch := shared.chans[name] - done := shared.done[name] - shared.mux.Unlock() - - if ch != nil && done != nil { - select { - case ch <- event: - case <-done: - } - } -} - -// run starts the goroutine in which the shared struct reads events from its -// Watcher's Event channel and sends the events to the appropriate Tail. -func (shared *InotifyTracker) run() { - watcher, err := fsnotify.NewWatcher() - if err != nil { - util.Fatal("failed to create Watcher") - } - shared.watcher = watcher - - for { - select { - case winfo := <-shared.watch: - shared.error <- shared.addWatch(winfo) - - case winfo := <-shared.remove: - shared.removeWatch(winfo) - - case event, open := <-shared.watcher.Events: - if !open { - return - } - shared.sendEvent(event) - - case err, open := <-shared.watcher.Errors: - if !open { - return - } else if err != nil { - sysErr, ok := err.(*os.SyscallError) - if !ok || sysErr.Err != syscall.EINTR { - logger.Printf("Error in Watcher Error channel: %s", err) - } - } - } - } -} diff --git a/vendor/github.com/hpcloud/tail/watch/polling.go b/vendor/github.com/hpcloud/tail/watch/polling.go deleted file mode 100644 index 49491f21d..000000000 --- a/vendor/github.com/hpcloud/tail/watch/polling.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package watch - -import ( - "os" - "runtime" - "time" - - "github.com/hpcloud/tail/util" - "gopkg.in/tomb.v1" -) - -// PollingFileWatcher polls the file for changes. -type PollingFileWatcher struct { - Filename string - Size int64 -} - -func NewPollingFileWatcher(filename string) *PollingFileWatcher { - fw := &PollingFileWatcher{filename, 0} - return fw -} - -var POLL_DURATION time.Duration - -func (fw *PollingFileWatcher) BlockUntilExists(t *tomb.Tomb) error { - for { - if _, err := os.Stat(fw.Filename); err == nil { - return nil - } else if !os.IsNotExist(err) { - return err - } - select { - case <-time.After(POLL_DURATION): - continue - case <-t.Dying(): - return tomb.ErrDying - } - } - panic("unreachable") -} - -func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { - origFi, err := os.Stat(fw.Filename) - if err != nil { - return nil, err - } - - changes := NewFileChanges() - var prevModTime time.Time - - // XXX: use tomb.Tomb to cleanly manage these goroutines. replace - // the fatal (below) with tomb's Kill. - - fw.Size = pos - - go func() { - prevSize := fw.Size - for { - select { - case <-t.Dying(): - return - default: - } - - time.Sleep(POLL_DURATION) - fi, err := os.Stat(fw.Filename) - if err != nil { - // Windows cannot delete a file if a handle is still open (tail keeps one open) - // so it gives access denied to anything trying to read it until all handles are released. - if os.IsNotExist(err) || (runtime.GOOS == "windows" && os.IsPermission(err)) { - // File does not exist (has been deleted). - changes.NotifyDeleted() - return - } - - // XXX: report this error back to the user - util.Fatal("Failed to stat file %v: %v", fw.Filename, err) - } - - // File got moved/renamed? - if !os.SameFile(origFi, fi) { - changes.NotifyDeleted() - return - } - - // File got truncated? - fw.Size = fi.Size() - if prevSize > 0 && prevSize > fw.Size { - changes.NotifyTruncated() - prevSize = fw.Size - continue - } - // File got bigger? - if prevSize > 0 && prevSize < fw.Size { - changes.NotifyModified() - prevSize = fw.Size - continue - } - prevSize = fw.Size - - // File was appended to (changed)? - modTime := fi.ModTime() - if modTime != prevModTime { - prevModTime = modTime - changes.NotifyModified() - } - } - }() - - return changes, nil -} - -func init() { - POLL_DURATION = 250 * time.Millisecond -} diff --git a/vendor/github.com/hpcloud/tail/watch/watch.go b/vendor/github.com/hpcloud/tail/watch/watch.go deleted file mode 100644 index 2e1783ef0..000000000 --- a/vendor/github.com/hpcloud/tail/watch/watch.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2015 HPE Software Inc. All rights reserved. -// Copyright (c) 2013 ActiveState Software Inc. All rights reserved. - -package watch - -import "gopkg.in/tomb.v1" - -// FileWatcher monitors file-level events. -type FileWatcher interface { - // BlockUntilExists blocks until the file comes into existence. - BlockUntilExists(*tomb.Tomb) error - - // ChangeEvents reports on changes to a file, be it modification, - // deletion, renames or truncations. Returned FileChanges group of - // channels will be closed, thus become unusable, after a deletion - // or truncation event. - // In order to properly report truncations, ChangeEvents requires - // the caller to pass their current offset in the file. - ChangeEvents(*tomb.Tomb, int64) (*FileChanges, error) -} diff --git a/vendor/github.com/hpcloud/tail/winfile/winfile.go b/vendor/github.com/hpcloud/tail/winfile/winfile.go deleted file mode 100644 index aa7e7bc5d..000000000 --- a/vendor/github.com/hpcloud/tail/winfile/winfile.go +++ /dev/null @@ -1,92 +0,0 @@ -// +build windows - -package winfile - -import ( - "os" - "syscall" - "unsafe" -) - -// issue also described here -//https://codereview.appspot.com/8203043/ - -// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L218 -func Open(path string, mode int, perm uint32) (fd syscall.Handle, err error) { - if len(path) == 0 { - return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND - } - pathp, err := syscall.UTF16PtrFromString(path) - if err != nil { - return syscall.InvalidHandle, err - } - var access uint32 - switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { - case syscall.O_RDONLY: - access = syscall.GENERIC_READ - case syscall.O_WRONLY: - access = syscall.GENERIC_WRITE - case syscall.O_RDWR: - access = syscall.GENERIC_READ | syscall.GENERIC_WRITE - } - if mode&syscall.O_CREAT != 0 { - access |= syscall.GENERIC_WRITE - } - if mode&syscall.O_APPEND != 0 { - access &^= syscall.GENERIC_WRITE - access |= syscall.FILE_APPEND_DATA - } - sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE) - var sa *syscall.SecurityAttributes - if mode&syscall.O_CLOEXEC == 0 { - sa = makeInheritSa() - } - var createmode uint32 - switch { - case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): - createmode = syscall.CREATE_NEW - case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): - createmode = syscall.CREATE_ALWAYS - case mode&syscall.O_CREAT == syscall.O_CREAT: - createmode = syscall.OPEN_ALWAYS - case mode&syscall.O_TRUNC == syscall.O_TRUNC: - createmode = syscall.TRUNCATE_EXISTING - default: - createmode = syscall.OPEN_EXISTING - } - h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0) - return h, e -} - -// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L211 -func makeInheritSa() *syscall.SecurityAttributes { - var sa syscall.SecurityAttributes - sa.Length = uint32(unsafe.Sizeof(sa)) - sa.InheritHandle = 1 - return &sa -} - -// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_windows.go#L133 -func OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) { - r, e := Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) - if e != nil { - return nil, e - } - return os.NewFile(uintptr(r), name), nil -} - -// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_posix.go#L61 -func syscallMode(i os.FileMode) (o uint32) { - o |= uint32(i.Perm()) - if i&os.ModeSetuid != 0 { - o |= syscall.S_ISUID - } - if i&os.ModeSetgid != 0 { - o |= syscall.S_ISGID - } - if i&os.ModeSticky != 0 { - o |= syscall.S_ISVTX - } - // No mapping for Go's ModeTemporary (plan9 only). - return -} -- cgit v1.2.3-54-g00ecf