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