119 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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
 | 
						|
}
 |