tail/watch/inotify.go

138 lines
2.7 KiB
Go
Raw Normal View History

// Copyright (c) 2015 HPE Software Inc. All rights reserved.
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
package watch
import (
"fmt"
2015-07-01 23:15:15 +08:00
"os"
"path/filepath"
2015-09-30 22:48:17 +08:00
"github.com/hpcloud/tail/util"
2015-09-11 07:47:13 +08:00
"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 {
2015-12-09 15:29:06 +08:00
fw := &InotifyFileWatcher{filepath.Clean(filename), 0}
return fw
}
2013-09-23 15:03:51 +08:00
func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
2015-12-09 15:29:06 +08:00
err := WatchCreate(fw.Filename)
if err != nil {
return err
}
2015-12-09 15:29:06 +08:00
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")
}
2015-12-24 16:47:46 +08:00
func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) {
err := Watch(fw.Filename)
if err != nil {
2015-12-24 16:47:46 +08:00
return nil, err
}
2015-12-24 16:47:46 +08:00
changes := NewFileChanges()
fw.Size = pos
go func() {
defer RemoveWatch(fw.Filename)
events := Events(fw.Filename)
for {
prevSize := fw.Size
2015-09-11 07:56:51 +08:00
var evt fsnotify.Event
var ok bool
select {
case evt, ok = <-events:
if !ok {
return
}
case <-t.Dying():
return
}
switch {
//With an open fd, unlink(fd) - inotify returns IN_ATTRIB (==fsnotify.Chmod)
case evt.Op&fsnotify.Chmod == fsnotify.Chmod:
if _, err := os.Stat(fw.Filename); err != nil {
if ! os.IsNotExist(err) {
return
}
}
fallthrough
2015-09-11 07:47:13 +08:00
case evt.Op&fsnotify.Remove == fsnotify.Remove:
fallthrough
2015-09-11 07:47:13 +08:00
case evt.Op&fsnotify.Rename == fsnotify.Rename:
changes.NotifyDeleted()
return
2015-09-11 07:47:13 +08:00
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()
2013-09-23 15:03:51 +08:00
} else {
changes.NotifyModified()
}
prevSize = fw.Size
}
}
}()
2015-12-24 16:47:46 +08:00
return changes, nil
}