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.

180 lines
3.3 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
  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. func (af *AutoFile) Close() error {
  72. af.closeTicker.Stop()
  73. close(af.closeTickerStopc)
  74. if af.hupc != nil {
  75. close(af.hupc)
  76. }
  77. return af.closeFile()
  78. }
  79. func (af *AutoFile) closeFileRoutine() {
  80. for {
  81. select {
  82. case <-af.closeTicker.C:
  83. af.closeFile()
  84. case <-af.closeTickerStopc:
  85. return
  86. }
  87. }
  88. }
  89. func (af *AutoFile) closeFile() (err error) {
  90. af.mtx.Lock()
  91. defer af.mtx.Unlock()
  92. file := af.file
  93. if file == nil {
  94. return nil
  95. }
  96. af.file = nil
  97. return file.Close()
  98. }
  99. func (af *AutoFile) Write(b []byte) (n int, err error) {
  100. af.mtx.Lock()
  101. defer af.mtx.Unlock()
  102. if af.file == nil {
  103. if err = af.openFile(); err != nil {
  104. return
  105. }
  106. }
  107. n, err = af.file.Write(b)
  108. return
  109. }
  110. func (af *AutoFile) Sync() error {
  111. af.mtx.Lock()
  112. defer af.mtx.Unlock()
  113. if af.file == nil {
  114. if err := af.openFile(); err != nil {
  115. return err
  116. }
  117. }
  118. return af.file.Sync()
  119. }
  120. func (af *AutoFile) openFile() error {
  121. file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms)
  122. if err != nil {
  123. return err
  124. }
  125. fileInfo, err := file.Stat()
  126. if err != nil {
  127. return err
  128. }
  129. if fileInfo.Mode() != autoFilePerms {
  130. return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
  131. }
  132. af.file = file
  133. return nil
  134. }
  135. func (af *AutoFile) Size() (int64, error) {
  136. af.mtx.Lock()
  137. defer af.mtx.Unlock()
  138. if af.file == nil {
  139. err := af.openFile()
  140. if err != nil {
  141. if err == os.ErrNotExist {
  142. return 0, nil
  143. }
  144. return -1, err
  145. }
  146. }
  147. stat, err := af.file.Stat()
  148. if err != nil {
  149. return -1, err
  150. }
  151. return stat.Size(), nil
  152. }