polling.go 2.5 KB

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