You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
2.8 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package autofile
  2. import (
  3. "os"
  4. "sync"
  5. "time"
  6. cmn "github.com/tendermint/tendermint/libs/common"
  7. "github.com/tendermint/tendermint/libs/errors"
  8. )
  9. /* AutoFile usage
  10. // Create/Append to ./autofile_test
  11. af, err := OpenAutoFile("autofile_test")
  12. if err != nil {
  13. panic(err)
  14. }
  15. // Stream of writes.
  16. // During this time, the file may be moved e.g. by logRotate.
  17. for i := 0; i < 60; i++ {
  18. af.Write([]byte(Fmt("LOOP(%v)", i)))
  19. time.Sleep(time.Second)
  20. }
  21. // Close the AutoFile
  22. err = af.Close()
  23. if err != nil {
  24. panic(err)
  25. }
  26. */
  27. const (
  28. autoFileOpenDuration = 1000 * time.Millisecond
  29. autoFilePerms = os.FileMode(0600)
  30. )
  31. // Automatically closes and re-opens file for writing.
  32. // This is useful for using a log file with the logrotate tool.
  33. type AutoFile struct {
  34. ID string
  35. Path string
  36. ticker *time.Ticker
  37. tickerStopped chan struct{} // closed when ticker is stopped
  38. mtx sync.Mutex
  39. file *os.File
  40. }
  41. func OpenAutoFile(path string) (af *AutoFile, err error) {
  42. af = &AutoFile{
  43. ID: cmn.RandStr(12) + ":" + path,
  44. Path: path,
  45. ticker: time.NewTicker(autoFileOpenDuration),
  46. tickerStopped: make(chan struct{}),
  47. }
  48. if err = af.openFile(); err != nil {
  49. return
  50. }
  51. go af.processTicks()
  52. sighupWatchers.addAutoFile(af)
  53. return
  54. }
  55. func (af *AutoFile) Close() error {
  56. af.ticker.Stop()
  57. close(af.tickerStopped)
  58. err := af.closeFile()
  59. sighupWatchers.removeAutoFile(af)
  60. return err
  61. }
  62. func (af *AutoFile) processTicks() {
  63. for {
  64. select {
  65. case <-af.ticker.C:
  66. af.closeFile()
  67. case <-af.tickerStopped:
  68. return
  69. }
  70. }
  71. }
  72. func (af *AutoFile) closeFile() (err error) {
  73. af.mtx.Lock()
  74. defer af.mtx.Unlock()
  75. file := af.file
  76. if file == nil {
  77. return nil
  78. }
  79. af.file = nil
  80. return file.Close()
  81. }
  82. func (af *AutoFile) Write(b []byte) (n int, err error) {
  83. af.mtx.Lock()
  84. defer af.mtx.Unlock()
  85. if af.file == nil {
  86. if err = af.openFile(); err != nil {
  87. return
  88. }
  89. }
  90. n, err = af.file.Write(b)
  91. return
  92. }
  93. func (af *AutoFile) Sync() error {
  94. af.mtx.Lock()
  95. defer af.mtx.Unlock()
  96. if af.file == nil {
  97. if err := af.openFile(); err != nil {
  98. return err
  99. }
  100. }
  101. return af.file.Sync()
  102. }
  103. func (af *AutoFile) openFile() error {
  104. file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms)
  105. if err != nil {
  106. return err
  107. }
  108. fileInfo, err := file.Stat()
  109. if err != nil {
  110. return err
  111. }
  112. if fileInfo.Mode() != autoFilePerms {
  113. return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
  114. }
  115. af.file = file
  116. return nil
  117. }
  118. func (af *AutoFile) Size() (int64, error) {
  119. af.mtx.Lock()
  120. defer af.mtx.Unlock()
  121. if af.file == nil {
  122. err := af.openFile()
  123. if err != nil {
  124. if err == os.ErrNotExist {
  125. return 0, nil
  126. }
  127. return -1, err
  128. }
  129. }
  130. stat, err := af.file.Stat()
  131. if err != nil {
  132. return -1, err
  133. }
  134. return stat.Size(), nil
  135. }