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.

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