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.

194 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. "path/filepath"
  6. "sync"
  7. "syscall"
  8. "time"
  9. tmrand "github.com/tendermint/tendermint/libs/rand"
  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. var err error
  51. path, err = filepath.Abs(path)
  52. if err != nil {
  53. return nil, err
  54. }
  55. af := &AutoFile{
  56. ID: tmrand.Str(12) + ":" + path,
  57. Path: path,
  58. closeTicker: time.NewTicker(autoFileClosePeriod),
  59. closeTickerStopc: make(chan struct{}),
  60. }
  61. if err := af.openFile(); err != nil {
  62. af.Close()
  63. return nil, err
  64. }
  65. // Close file on SIGHUP.
  66. af.hupc = make(chan os.Signal, 1)
  67. signal.Notify(af.hupc, syscall.SIGHUP)
  68. go func() {
  69. for range af.hupc {
  70. af.closeFile()
  71. }
  72. }()
  73. go af.closeFileRoutine()
  74. return af, nil
  75. }
  76. // Close shuts down the closing goroutine, SIGHUP handler and closes the
  77. // AutoFile.
  78. func (af *AutoFile) Close() error {
  79. af.closeTicker.Stop()
  80. close(af.closeTickerStopc)
  81. if af.hupc != nil {
  82. close(af.hupc)
  83. }
  84. return af.closeFile()
  85. }
  86. func (af *AutoFile) closeFileRoutine() {
  87. for {
  88. select {
  89. case <-af.closeTicker.C:
  90. af.closeFile()
  91. case <-af.closeTickerStopc:
  92. return
  93. }
  94. }
  95. }
  96. func (af *AutoFile) closeFile() (err error) {
  97. af.mtx.Lock()
  98. defer af.mtx.Unlock()
  99. file := af.file
  100. if file == nil {
  101. return nil
  102. }
  103. af.file = nil
  104. return file.Close()
  105. }
  106. // Write writes len(b) bytes to the AutoFile. It returns the number of bytes
  107. // written and an error, if any. Write returns a non-nil error when n !=
  108. // len(b).
  109. // Opens AutoFile if needed.
  110. func (af *AutoFile) Write(b []byte) (n int, err error) {
  111. af.mtx.Lock()
  112. defer af.mtx.Unlock()
  113. if af.file == nil {
  114. if err = af.openFile(); err != nil {
  115. return
  116. }
  117. }
  118. n, err = af.file.Write(b)
  119. return
  120. }
  121. // Sync commits the current contents of the file to stable storage. Typically,
  122. // this means flushing the file system's in-memory copy of recently written
  123. // data to disk.
  124. // Opens AutoFile if needed.
  125. func (af *AutoFile) Sync() error {
  126. af.mtx.Lock()
  127. defer af.mtx.Unlock()
  128. if af.file == nil {
  129. if err := af.openFile(); err != nil {
  130. return err
  131. }
  132. }
  133. return af.file.Sync()
  134. }
  135. func (af *AutoFile) openFile() error {
  136. file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms)
  137. if err != nil {
  138. return err
  139. }
  140. // fileInfo, err := file.Stat()
  141. // if err != nil {
  142. // return err
  143. // }
  144. // if fileInfo.Mode() != autoFilePerms {
  145. // return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
  146. // }
  147. af.file = file
  148. return nil
  149. }
  150. // Size returns the size of the AutoFile. It returns -1 and an error if fails
  151. // get stats or open file.
  152. // Opens AutoFile if needed.
  153. func (af *AutoFile) Size() (int64, error) {
  154. af.mtx.Lock()
  155. defer af.mtx.Unlock()
  156. if af.file == nil {
  157. if err := af.openFile(); err != nil {
  158. return -1, err
  159. }
  160. }
  161. stat, err := af.file.Stat()
  162. if err != nil {
  163. return -1, err
  164. }
  165. return stat.Size(), nil
  166. }