github.com/durkenhe/tailf@v0.0.0-20210829070326-d7341c377de4/watch/polling.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 "os" 8 "runtime" 9 "time" 10 11 "github.com/durkenhe/tailf/util" 12 "gopkg.in/tomb.v1" 13 ) 14 15 // PollingFileWatcher polls the file for changes. 16 type PollingFileWatcher struct { 17 Filename string 18 Size int64 19 } 20 21 func NewPollingFileWatcher(filename string) *PollingFileWatcher { 22 fw := &PollingFileWatcher{filename, 0} 23 return fw 24 } 25 26 var POLL_DURATION time.Duration 27 28 func (fw *PollingFileWatcher) BlockUntilExists(t *tomb.Tomb) error { 29 for { 30 if _, err := os.Stat(fw.Filename); err == nil { 31 return nil 32 } else if !os.IsNotExist(err) { 33 return err 34 } 35 select { 36 case <-time.After(POLL_DURATION): 37 continue 38 case <-t.Dying(): 39 return tomb.ErrDying 40 } 41 } 42 panic("unreachable") 43 } 44 45 func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChanges, error) { 46 origFi, err := os.Stat(fw.Filename) 47 if err != nil { 48 return nil, err 49 } 50 51 changes := NewFileChanges() 52 var prevModTime time.Time 53 54 // XXX: use tomb.Tomb to cleanly manage these goroutines. replace 55 // the fatal (below) with tomb's Kill. 56 57 fw.Size = pos 58 59 go func() { 60 prevSize := fw.Size 61 for { 62 select { 63 case <-t.Dying(): 64 return 65 default: 66 } 67 68 time.Sleep(POLL_DURATION) 69 fi, err := os.Stat(fw.Filename) 70 if err != nil { 71 // Windows cannot delete a file if a handle is still open (tail keeps one open) 72 // so it gives access denied to anything trying to read it until all handles are released. 73 if os.IsNotExist(err) || (runtime.GOOS == "windows" && os.IsPermission(err)) { 74 // File does not exist (has been deleted). 75 changes.NotifyDeleted() 76 return 77 } 78 79 // XXX: report this error back to the user 80 util.Fatal("Failed to stat file %v: %v", fw.Filename, err) 81 } 82 83 // File got moved/renamed? 84 if !os.SameFile(origFi, fi) { 85 changes.NotifyDeleted() 86 return 87 } 88 89 // File got truncated? 90 fw.Size = fi.Size() 91 if prevSize > 0 && prevSize > fw.Size { 92 changes.NotifyTruncated() 93 prevSize = fw.Size 94 continue 95 } 96 // File got bigger? 97 if prevSize > 0 && prevSize < fw.Size { 98 changes.NotifyModified() 99 prevSize = fw.Size 100 continue 101 } 102 prevSize = fw.Size 103 104 // File was appended to (changed)? 105 modTime := fi.ModTime() 106 if modTime != prevModTime { 107 prevModTime = modTime 108 changes.NotifyModified() 109 } 110 } 111 }() 112 113 return changes, nil 114 } 115 116 func init() { 117 POLL_DURATION = 250 * time.Millisecond 118 }