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