golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/ringlogger/ringlogger.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package ringlogger 7 8 import ( 9 "bytes" 10 "fmt" 11 "io" 12 "os" 13 "runtime" 14 "strconv" 15 "sync/atomic" 16 "time" 17 "unsafe" 18 19 "golang.org/x/sys/windows" 20 ) 21 22 const ( 23 maxLogLineLength = 512 24 maxTagLength = 5 25 maxLines = 2048 26 magic = 0xbadbabe 27 ) 28 29 type logLine struct { 30 timeNs int64 31 line [maxLogLineLength]byte 32 } 33 34 type logMem struct { 35 magic uint32 36 nextIndex uint32 37 lines [maxLines]logLine 38 } 39 40 type Ringlogger struct { 41 tag string 42 file *os.File 43 mapping windows.Handle 44 log *logMem 45 readOnly bool 46 } 47 48 func NewRinglogger(filename, tag string) (*Ringlogger, error) { 49 if len(tag) > maxTagLength { 50 return nil, windows.ERROR_LABEL_TOO_LONG 51 } 52 file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0o600) 53 if err != nil { 54 return nil, err 55 } 56 err = file.Truncate(int64(unsafe.Sizeof(logMem{}))) 57 if err != nil { 58 return nil, err 59 } 60 mapping, err := windows.CreateFileMapping(windows.Handle(file.Fd()), nil, windows.PAGE_READWRITE, 0, 0, nil) 61 if err != nil && err != windows.ERROR_ALREADY_EXISTS { 62 return nil, err 63 } 64 rl, err := newRingloggerFromMappingHandle(mapping, tag, windows.FILE_MAP_WRITE) 65 if err != nil { 66 windows.CloseHandle(mapping) 67 return nil, err 68 } 69 rl.file = file 70 return rl, nil 71 } 72 73 func NewRingloggerFromInheritedMappingHandle(handleStr, tag string) (*Ringlogger, error) { 74 handle, err := strconv.ParseUint(handleStr, 10, 64) 75 if err != nil { 76 return nil, err 77 } 78 return newRingloggerFromMappingHandle(windows.Handle(handle), tag, windows.FILE_MAP_READ) 79 } 80 81 func newRingloggerFromMappingHandle(mappingHandle windows.Handle, tag string, access uint32) (*Ringlogger, error) { 82 view, err := windows.MapViewOfFile(mappingHandle, access, 0, 0, 0) 83 if err != nil { 84 return nil, err 85 } 86 log := (*logMem)(unsafe.Pointer(view)) 87 if log.magic != magic { 88 bytes := (*[unsafe.Sizeof(logMem{})]byte)(unsafe.Pointer(log)) 89 for i := range bytes { 90 bytes[i] = 0 91 } 92 log.magic = magic 93 windows.FlushViewOfFile(view, uintptr(len(bytes))) 94 } 95 96 rl := &Ringlogger{ 97 tag: tag, 98 mapping: mappingHandle, 99 log: log, 100 readOnly: access&windows.FILE_MAP_WRITE == 0, 101 } 102 runtime.SetFinalizer(rl, (*Ringlogger).Close) 103 return rl, nil 104 } 105 106 func (rl *Ringlogger) Write(p []byte) (n int, err error) { 107 // Race: This isn't synchronized with the fetch_add below, so items might be slightly out of order. 108 ts := time.Now().UnixNano() 109 return rl.WriteWithTimestamp(p, ts) 110 } 111 112 func (rl *Ringlogger) WriteWithTimestamp(p []byte, ts int64) (n int, err error) { 113 if rl.readOnly { 114 return 0, io.ErrShortWrite 115 } 116 ret := len(p) 117 p = bytes.TrimSpace(p) 118 if len(p) == 0 { 119 return ret, nil 120 } 121 122 if rl.log == nil { 123 return 0, io.EOF 124 } 125 126 // Race: More than maxLines writers and this will clash. 127 index := atomic.AddUint32(&rl.log.nextIndex, 1) - 1 128 line := &rl.log.lines[index%maxLines] 129 130 // Race: Before this line executes, we'll display old data after new data. 131 atomic.StoreInt64(&line.timeNs, 0) 132 for i := range line.line { 133 line.line[i] = 0 134 } 135 136 textLen := 3 + len(p) + len(rl.tag) 137 if textLen > maxLogLineLength-1 { 138 p = p[:maxLogLineLength-1-3-len(rl.tag)] 139 textLen = maxLogLineLength - 1 140 } 141 line.line[textLen] = 0 142 line.line[0] = 0 // Null out the beginning and only let it extend after the other writes have completed 143 copy(line.line[1:], rl.tag) 144 line.line[1+len(rl.tag)] = ']' 145 line.line[2+len(rl.tag)] = ' ' 146 copy(line.line[3+len(rl.tag):], p[:]) 147 line.line[0] = '[' 148 atomic.StoreInt64(&line.timeNs, ts) 149 150 return ret, nil 151 } 152 153 func (rl *Ringlogger) WriteTo(out io.Writer) (n int64, err error) { 154 if rl.log == nil { 155 return 0, io.EOF 156 } 157 log := *rl.log 158 i := log.nextIndex 159 for l := uint32(0); l < maxLines; l++ { 160 line := &log.lines[(i+l)%maxLines] 161 if line.timeNs == 0 { 162 continue 163 } 164 index := bytes.IndexByte(line.line[:], 0) 165 if index < 1 { 166 continue 167 } 168 var bytes int 169 bytes, err = fmt.Fprintf(out, "%s: %s\n", time.Unix(0, line.timeNs).Format("2006-01-02 15:04:05.000000"), line.line[:index]) 170 if err != nil { 171 return 172 } 173 n += int64(bytes) 174 } 175 return 176 } 177 178 const CursorAll = ^uint32(0) 179 180 type FollowLine struct { 181 Line string 182 Stamp time.Time 183 } 184 185 func (rl *Ringlogger) FollowFromCursor(cursor uint32) (followLines []FollowLine, nextCursor uint32) { 186 followLines = make([]FollowLine, 0, maxLines) 187 nextCursor = cursor 188 189 if rl.log == nil { 190 return 191 } 192 log := *rl.log 193 194 i := cursor 195 if cursor == CursorAll { 196 i = log.nextIndex 197 } 198 199 for l := 0; l < maxLines; l++ { 200 line := &log.lines[i%maxLines] 201 if cursor != CursorAll && i%maxLines == log.nextIndex%maxLines { 202 break 203 } 204 if line.timeNs == 0 { 205 if cursor == CursorAll { 206 i++ 207 continue 208 } else { 209 break 210 } 211 } 212 index := bytes.IndexByte(line.line[:], 0) 213 if index > 0 { 214 followLines = append(followLines, FollowLine{string(line.line[:index]), time.Unix(0, line.timeNs)}) 215 } 216 i++ 217 nextCursor = i % maxLines 218 } 219 return 220 } 221 222 func (rl *Ringlogger) Close() error { 223 if rl.file != nil { 224 rl.file.Close() 225 rl.file = nil 226 } 227 if rl.log != nil { 228 windows.UnmapViewOfFile((uintptr)(unsafe.Pointer(rl.log))) 229 rl.log = nil 230 } 231 if rl.mapping != 0 { 232 windows.CloseHandle(rl.mapping) 233 rl.mapping = 0 234 } 235 return nil 236 } 237 238 func (rl *Ringlogger) ExportInheritableMappingHandle() (handleToClose windows.Handle, err error) { 239 handleToClose, err = windows.CreateFileMapping(windows.Handle(rl.file.Fd()), nil, windows.PAGE_READONLY, 0, 0, nil) 240 if err != nil && err != windows.ERROR_ALREADY_EXISTS { 241 return 242 } 243 err = windows.SetHandleInformation(handleToClose, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT) 244 if err != nil { 245 windows.CloseHandle(handleToClose) 246 handleToClose = 0 247 return 248 } 249 return 250 }