github.com/dasong2410/tail@v1.0.0/watch/inotify.go (about) 1 // Copyright (c) 2015 HPE Software Inc. All rights reserved. 2 // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. 3 4 package watch 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 11 "github.com/hpcloud/tail/util" 12 13 "gopkg.in/fsnotify.v1" 14 "gopkg.in/tomb.v1" 15 ) 16 17 // InotifyFileWatcher uses inotify to monitor file changes. 18 type InotifyFileWatcher struct { 19 Filename string 20 Size int64 21 } 22 23 func NewInotifyFileWatcher(filename string) *InotifyFileWatcher { 24 fw := &InotifyFileWatcher{filepath.Clean(filename), 0} 25 return fw 26 } 27 28 func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error { 29 err := WatchCreate(fw.Filename) 30 if err != nil { 31 return err 32 } 33 defer RemoveWatchCreate(fw.Filename) 34 35 // Do a real check now as the file might have been created before 36 // calling `WatchFlags` above. 37 if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) { 38 // file exists, or stat returned an error. 39 return err 40 } 41 42 events := Events(fw.Filename) 43 44 for { 45 select { 46 case evt, ok := <-events: 47 if !ok { 48 return fmt.Errorf("inotify watcher has been closed") 49 } 50 evtName, err := filepath.Abs(evt.Name) 51 if err != nil { 52 return err 53 } 54 fwFilename, err := filepath.Abs(fw.Filename) 55 if err != nil { 56 return err 57 } 58 if evtName == fwFilename { 59 return nil 60 } 61 case <-t.Dying(): 62 return tomb.ErrDying 63 } 64 } 65 panic("unreachable") 66 } 67 68 func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { 69 err := Watch(fw.Filename) 70 if err != nil { 71 return nil, err 72 } 73 74 changes := NewFileChanges() 75 fw.Size = pos 76 77 go func() { 78 defer RemoveWatch(fw.Filename) 79 80 events := Events(fw.Filename) 81 82 for { 83 prevSize := fw.Size 84 85 var evt fsnotify.Event 86 var ok bool 87 88 select { 89 case evt, ok = <-events: 90 if !ok { 91 return 92 } 93 case <-t.Dying(): 94 return 95 } 96 97 switch { 98 case evt.Op&fsnotify.Remove == fsnotify.Remove: 99 fallthrough 100 101 case evt.Op&fsnotify.Rename == fsnotify.Rename: 102 changes.NotifyDeleted() 103 return 104 105 case evt.Op&fsnotify.Write == fsnotify.Write: 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, nil 128 }