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.

189 lines
3.9 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
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. "os/signal"
  5. "sync"
  6. "syscall"
  7. "time"
  8. cmn "github.com/tendermint/tendermint/libs/common"
  9. "github.com/tendermint/tendermint/libs/errors"
  10. )
  11. /* AutoFile usage
  12. // Create/Append to ./autofile_test
  13. af, err := OpenAutoFile("autofile_test")
  14. if err != nil {
  15. panic(err)
  16. }
  17. // Stream of writes.
  18. // During this time, the file may be moved e.g. by logRotate.
  19. for i := 0; i < 60; i++ {
  20. af.Write([]byte(Fmt("LOOP(%v)", i)))
  21. time.Sleep(time.Second)
  22. }
  23. // Close the AutoFile
  24. err = af.Close()
  25. if err != nil {
  26. panic(err)
  27. }
  28. */
  29. const (
  30. autoFileClosePeriod = 1000 * time.Millisecond
  31. autoFilePerms = os.FileMode(0600)
  32. )
  33. // AutoFile automatically closes and re-opens file for writing. The file is
  34. // automatically setup to close itself every 1s and upon receiving SIGHUP.
  35. //
  36. // This is useful for using a log file with the logrotate tool.
  37. type AutoFile struct {
  38. ID string
  39. Path string
  40. closeTicker *time.Ticker
  41. closeTickerStopc chan struct{} // closed when closeTicker is stopped
  42. hupc chan os.Signal
  43. mtx sync.Mutex
  44. file *os.File
  45. }
  46. // OpenAutoFile creates an AutoFile in the path (with random ID). If there is
  47. // an error, it will be of type *PathError or *ErrPermissionsChanged (if file's
  48. // permissions got changed (should be 0600)).
  49. func OpenAutoFile(path string) (*AutoFile, error) {
  50. af := &AutoFile{
  51. ID: cmn.RandStr(12) + ":" + path,
  52. Path: path,
  53. closeTicker: time.NewTicker(autoFileClosePeriod),
  54. closeTickerStopc: make(chan struct{}),
  55. }
  56. if err := af.openFile(); err != nil {
  57. af.Close()
  58. return nil, err
  59. }
  60. // Close file on SIGHUP.
  61. af.hupc = make(chan os.Signal, 1)
  62. signal.Notify(af.hupc, syscall.SIGHUP)
  63. go func() {
  64. for range af.hupc {
  65. af.closeFile()
  66. }
  67. }()
  68. go af.closeFileRoutine()
  69. return af, nil
  70. }
  71. // Close shuts down the closing goroutine, SIGHUP handler and closes the
  72. // AutoFile.
  73. func (af *AutoFile) Close() error {
  74. af.closeTicker.Stop()
  75. close(af.closeTickerStopc)
  76. if af.hupc != nil {
  77. close(af.hupc)
  78. }
  79. return af.closeFile()
  80. }
  81. func (af *AutoFile) closeFileRoutine() {
  82. for {
  83. select {
  84. case <-af.closeTicker.C:
  85. af.closeFile()
  86. case <-af.closeTickerStopc:
  87. return
  88. }
  89. }
  90. }
  91. func (af *AutoFile) closeFile() (err error) {
  92. af.mtx.Lock()
  93. defer af.mtx.Unlock()
  94. file := af.file
  95. if file == nil {
  96. return nil
  97. }
  98. af.file = nil
  99. return file.Close()
  100. }
  101. // Write writes len(b) bytes to the AutoFile. It returns the number of bytes
  102. // written and an error, if any. Write returns a non-nil error when n !=
  103. // len(b).
  104. // Opens AutoFile if needed.
  105. func (af *AutoFile) Write(b []byte) (n int, err error) {
  106. af.mtx.Lock()
  107. defer af.mtx.Unlock()
  108. if af.file == nil {
  109. if err = af.openFile(); err != nil {
  110. return
  111. }
  112. }
  113. n, err = af.file.Write(b)
  114. return
  115. }
  116. // Sync commits the current contents of the file to stable storage. Typically,
  117. // this means flushing the file system's in-memory copy of recently written
  118. // data to disk.
  119. // Opens AutoFile if needed.
  120. func (af *AutoFile) Sync() error {
  121. af.mtx.Lock()
  122. defer af.mtx.Unlock()
  123. if af.file == nil {
  124. if err := af.openFile(); err != nil {
  125. return err
  126. }
  127. }
  128. return af.file.Sync()
  129. }
  130. func (af *AutoFile) openFile() error {
  131. file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms)
  132. if err != nil {
  133. return err
  134. }
  135. fileInfo, err := file.Stat()
  136. if err != nil {
  137. return err
  138. }
  139. if fileInfo.Mode() != autoFilePerms {
  140. return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
  141. }
  142. af.file = file
  143. return nil
  144. }
  145. // Size returns the size of the AutoFile. It returns -1 and an error if fails
  146. // get stats or open file.
  147. // Opens AutoFile if needed.
  148. func (af *AutoFile) Size() (int64, error) {
  149. af.mtx.Lock()
  150. defer af.mtx.Unlock()
  151. if af.file == nil {
  152. if err := af.openFile(); err != nil {
  153. return -1, err
  154. }
  155. }
  156. stat, err := af.file.Stat()
  157. if err != nil {
  158. return -1, err
  159. }
  160. return stat.Size(), nil
  161. }