123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- // 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() {
- events := Events(fw.Filename)
- for {
- prevSize := fw.Size
- var evt fsnotify.Event
- var ok bool
- select {
- case evt, ok = <-events:
- if !ok {
- RemoveWatch(fw.Filename)
- return
- }
- case <-t.Dying():
- RemoveWatch(fw.Filename)
- return
- }
- switch {
- case evt.Op&fsnotify.Remove == fsnotify.Remove:
- fallthrough
- case evt.Op&fsnotify.Rename == fsnotify.Rename:
- RemoveWatch(fw.Filename)
- changes.NotifyDeleted()
- return
- //With an open fd, unlink(fd) - inotify returns IN_ATTRIB (==fsnotify.Chmod)
- case evt.Op&fsnotify.Chmod == fsnotify.Chmod:
- fallthrough
- case evt.Op&fsnotify.Write == fsnotify.Write:
- fi, err := os.Stat(fw.Filename)
- if err != nil {
- if os.IsNotExist(err) {
- RemoveWatch(fw.Filename)
- 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
- }
|