github.com/unikumab/tail@v1.0.1-0.20180514194441-a1dbeea552b7/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/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 79 events := Events(fw.Filename) 80 81 for { 82 prevSize := fw.Size 83 84 var evt fsnotify.Event 85 var ok bool 86 87 select { 88 case evt, ok = <-events: 89 if !ok { 90 RemoveWatch(fw.Filename) 91 return 92 } 93 case <-t.Dying(): 94 RemoveWatch(fw.Filename) 95 return 96 } 97 98 switch { 99 case evt.Op&fsnotify.Remove == fsnotify.Remove: 100 fallthrough 101 102 case evt.Op&fsnotify.Rename == fsnotify.Rename: 103 RemoveWatch(fw.Filename) 104 changes.NotifyDeleted() 105 return 106 107 //With an open fd, unlink(fd) - inotify returns IN_ATTRIB (==fsnotify.Chmod) 108 case evt.Op&fsnotify.Chmod == fsnotify.Chmod: 109 fallthrough 110 111 case evt.Op&fsnotify.Write == fsnotify.Write: 112 fi, err := os.Stat(fw.Filename) 113 if err != nil { 114 if os.IsNotExist(err) { 115 RemoveWatch(fw.Filename) 116 changes.NotifyDeleted() 117 return 118 } 119 // XXX: report this error back to the user 120 util.Fatal("Failed to stat file %v: %v", fw.Filename, err) 121 } 122 fw.Size = fi.Size() 123 124 if prevSize > 0 && prevSize > fw.Size { 125 changes.NotifyTruncated() 126 } else { 127 changes.NotifyModified() 128 } 129 prevSize = fw.Size 130 } 131 } 132 }() 133 134 return changes, nil 135 }