github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/autofile/autofile.go.bak (about) 1 package autofile 2 3 import ( 4 "bufio" 5 "os" 6 "os/signal" 7 "sync" 8 "syscall" 9 "time" 10 11 "github.com/gnolang/gno/tm2/pkg/random" 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 buf *bufio.Writer 55 file *os.File 56 } 57 58 // OpenAutoFile creates an AutoFile in the path (with random ID). If there is 59 // an error, it will be of type *PathError or *ErrPermissionsChanged (if file's 60 // permissions got changed (should be 0600)). 61 func OpenAutoFile(path string) (*AutoFile, error) { 62 af := &AutoFile{ 63 ID: random.RandStr(12) + ":" + path, 64 Path: path, 65 closeTicker: time.NewTicker(autoFileClosePeriod), 66 closeTickerStopc: make(chan struct{}), 67 } 68 if err := af.openFile(); err != nil { 69 af.Close() 70 return nil, err 71 } 72 73 // Close file on SIGHUP. 74 af.hupc = make(chan os.Signal, 1) 75 signal.Notify(af.hupc, syscall.SIGHUP) 76 go func() { 77 for range af.hupc { 78 af.closeFile() 79 } 80 }() 81 82 go af.closeFileRoutine() 83 84 return af, nil 85 } 86 87 // Close shuts down the closing goroutine, SIGHUP handler and closes the 88 // AutoFile. 89 func (af *AutoFile) Close() error { 90 af.closeTicker.Stop() 91 close(af.closeTickerStopc) 92 if af.hupc != nil { 93 close(af.hupc) 94 } 95 return af.closeFile() 96 } 97 98 func (af *AutoFile) closeFileRoutine() { 99 for { 100 select { 101 case <-af.closeTicker.C: 102 af.closeFile() 103 case <-af.closeTickerStopc: 104 return 105 } 106 } 107 } 108 109 func (af *AutoFile) closeFile() (err error) { 110 af.mtx.Lock() 111 defer af.mtx.Unlock() 112 113 if af.file == nil { 114 return nil 115 } 116 if af.buf == nil { 117 panic("should not happen") 118 } 119 120 // sync before closing the file. 121 af.sync() 122 123 // close file and return error. 124 err = af.file.Close() 125 if err != nil { 126 return err 127 } else { 128 af.buf = nil 129 af.file = nil 130 return nil 131 } 132 } 133 134 // Write writes len(b) bytes to the AutoFile. It returns the number of bytes 135 // written and an error, if any. Write returns a non-nil error when n != 136 // len(b). 137 // Opens AutoFile if needed. 138 func (af *AutoFile) Write(b []byte) (n int, err error) { 139 af.mtx.Lock() 140 defer af.mtx.Unlock() 141 142 if af.file == nil { 143 if err = af.openFile(); err != nil { 144 return 145 } 146 } 147 148 n, err = af.buf.Write(b) 149 return 150 } 151 152 // Sync commits the current contents of the file to stable storage. Typically, 153 // this means flushing the file system's in-memory copy of recently written 154 // data to disk. 155 // Opens AutoFile if needed. 156 func (af *AutoFile) Sync() error { 157 af.mtx.Lock() 158 defer af.mtx.Unlock() 159 160 return af.sync() 161 } 162 163 func (af *AutoFile) sync() error { 164 if af.file == nil { 165 if err := af.openFile(); err != nil { 166 return err 167 } 168 } 169 err := af.buf.Flush() 170 if err != nil { 171 return err 172 } 173 return af.file.Sync() 174 } 175 176 func (af *AutoFile) openFile() error { 177 file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms) 178 if err != nil { 179 return err 180 } 181 // fileInfo, err := file.Stat() 182 // if err != nil { 183 // return err 184 // } 185 // if fileInfo.Mode() != autoFilePerms { 186 // return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms) 187 // } 188 if af.buf != nil { 189 panic("should not happen") 190 } 191 af.buf = bufio.NewWriterSize(file, 1024*64) 192 af.file = file 193 return nil 194 } 195 196 // Size returns the size of the AutoFile. It returns -1 and an error if fails 197 // get stats or open file. 198 // Opens AutoFile if needed. 199 func (af *AutoFile) Size() (int64, error) { 200 af.mtx.Lock() 201 defer af.mtx.Unlock() 202 203 if af.file == nil { 204 if err := af.openFile(); err != nil { 205 return -1, err 206 } 207 } 208 209 filestat, err := af.file.Stat() 210 if err != nil { 211 return -1, err 212 } 213 filesize := filestat.Size() 214 bufsize := int64(af.buf.Buffered()) 215 return bufsize + filesize, nil 216 }