github.com/moontrade/unsafe@v0.9.1/capture/capture.go (about) 1 package capture 2 3 /* 4 #include <stdio.h> 5 #include <stdlib.h> 6 */ 7 import "C" 8 9 import ( 10 "bufio" 11 "bytes" 12 "io" 13 "os" 14 "sync" 15 "syscall" 16 ) 17 18 var lockStdFileDescriptorsSwapping sync.Mutex 19 20 // Capture captures stderr and stdout of a given function call. 21 func Capture(call func()) (output []byte, err error) { 22 originalStdErr, originalStdOut := os.Stderr, os.Stdout 23 defer func() { 24 lockStdFileDescriptorsSwapping.Lock() 25 26 os.Stderr, os.Stdout = originalStdErr, originalStdOut 27 28 lockStdFileDescriptorsSwapping.Unlock() 29 }() 30 31 r, w, err := os.Pipe() 32 if err != nil { 33 return nil, err 34 } 35 defer func() { 36 e := r.Close() 37 if e != nil { 38 err = e 39 } 40 if w != nil { 41 e = w.Close() 42 if err != nil { 43 err = e 44 } 45 } 46 }() 47 48 lockStdFileDescriptorsSwapping.Lock() 49 50 os.Stderr, os.Stdout = w, w 51 52 lockStdFileDescriptorsSwapping.Unlock() 53 54 out := make(chan []byte) 55 go func() { 56 defer func() { 57 // If there is a panic in the function call, copying from "r" does not work anymore. 58 _ = recover() 59 }() 60 61 var b bytes.Buffer 62 63 _, err := io.Copy(&b, r) 64 if err != nil { 65 panic(err) 66 } 67 68 out <- b.Bytes() 69 }() 70 71 call() 72 73 err = w.Close() 74 if err != nil { 75 return nil, err 76 } 77 w = nil 78 79 return <-out, err 80 } 81 82 // CaptureWithCGo captures stderr and stdout as well as stderr and stdout of C of a given function call. 83 func CaptureWithCGo(call func()) (output []byte, err error) { 84 lockStdFileDescriptorsSwapping.Lock() 85 86 originalStdout, e := syscall.Dup(syscall.Stdout) 87 if e != nil { 88 lockStdFileDescriptorsSwapping.Unlock() 89 90 return nil, e 91 } 92 93 originalStderr, e := syscall.Dup(syscall.Stderr) 94 if e != nil { 95 lockStdFileDescriptorsSwapping.Unlock() 96 97 return nil, e 98 } 99 100 lockStdFileDescriptorsSwapping.Unlock() 101 102 defer func() { 103 lockStdFileDescriptorsSwapping.Lock() 104 105 if e := syscall.Dup2(originalStdout, syscall.Stdout); e != nil { 106 lockStdFileDescriptorsSwapping.Unlock() 107 108 err = e 109 } 110 if e := syscall.Close(originalStdout); e != nil { 111 lockStdFileDescriptorsSwapping.Unlock() 112 113 err = e 114 } 115 if e := syscall.Dup2(originalStderr, syscall.Stderr); e != nil { 116 lockStdFileDescriptorsSwapping.Unlock() 117 118 err = e 119 } 120 if e := syscall.Close(originalStderr); e != nil { 121 lockStdFileDescriptorsSwapping.Unlock() 122 123 err = e 124 } 125 126 lockStdFileDescriptorsSwapping.Unlock() 127 }() 128 129 r, w, err := os.Pipe() 130 if err != nil { 131 return nil, err 132 } 133 defer func() { 134 e := r.Close() 135 if e != nil { 136 err = e 137 } 138 if w != nil { 139 e = w.Close() 140 if err != nil { 141 err = e 142 } 143 } 144 }() 145 146 lockStdFileDescriptorsSwapping.Lock() 147 148 if e := syscall.Dup2(int(w.Fd()), syscall.Stdout); e != nil { 149 lockStdFileDescriptorsSwapping.Unlock() 150 151 return nil, e 152 } 153 if e := syscall.Dup2(int(w.Fd()), syscall.Stderr); e != nil { 154 lockStdFileDescriptorsSwapping.Unlock() 155 156 return nil, e 157 } 158 159 lockStdFileDescriptorsSwapping.Unlock() 160 161 out := make(chan []byte) 162 go func() { 163 defer func() { 164 // If there is a panic in the function call, copying from "r" does not work anymore. 165 _ = recover() 166 }() 167 168 var b bytes.Buffer 169 170 _, err := io.Copy(&b, r) 171 if err != nil { 172 panic(err) 173 } 174 175 out <- b.Bytes() 176 }() 177 178 call() 179 180 lockStdFileDescriptorsSwapping.Lock() 181 182 C.fflush(C.stdout) 183 184 err = w.Close() 185 if err != nil { 186 lockStdFileDescriptorsSwapping.Unlock() 187 188 return nil, err 189 } 190 w = nil 191 192 if e := syscall.Close(syscall.Stdout); e != nil { 193 lockStdFileDescriptorsSwapping.Unlock() 194 195 return nil, e 196 } 197 if e := syscall.Close(syscall.Stderr); e != nil { 198 lockStdFileDescriptorsSwapping.Unlock() 199 200 return nil, e 201 } 202 203 lockStdFileDescriptorsSwapping.Unlock() 204 205 return <-out, err 206 } 207 208 type Msg struct { 209 Line string 210 } 211 212 // CaptureWithCGo captures stderr and stdout as well as stderr and stdout of C of a given function call. 213 func CaptureWithCGoChan(ch chan string, call func()) (err error) { 214 lockStdFileDescriptorsSwapping.Lock() 215 216 originalStdout, e := syscall.Dup(syscall.Stdout) 217 if e != nil { 218 lockStdFileDescriptorsSwapping.Unlock() 219 220 return e 221 } 222 223 originalStderr, e := syscall.Dup(syscall.Stderr) 224 if e != nil { 225 lockStdFileDescriptorsSwapping.Unlock() 226 227 return e 228 } 229 230 lockStdFileDescriptorsSwapping.Unlock() 231 232 defer func() { 233 lockStdFileDescriptorsSwapping.Lock() 234 235 if e := syscall.Dup2(originalStdout, syscall.Stdout); e != nil { 236 lockStdFileDescriptorsSwapping.Unlock() 237 238 err = e 239 } 240 if e := syscall.Close(originalStdout); e != nil { 241 lockStdFileDescriptorsSwapping.Unlock() 242 243 err = e 244 } 245 if e := syscall.Dup2(originalStderr, syscall.Stderr); e != nil { 246 lockStdFileDescriptorsSwapping.Unlock() 247 248 err = e 249 } 250 if e := syscall.Close(originalStderr); e != nil { 251 lockStdFileDescriptorsSwapping.Unlock() 252 253 err = e 254 } 255 256 lockStdFileDescriptorsSwapping.Unlock() 257 }() 258 259 r, w, err := os.Pipe() 260 if err != nil { 261 return err 262 } 263 defer func() { 264 e := r.Close() 265 if e != nil { 266 err = e 267 } 268 if w != nil { 269 e = w.Close() 270 if err != nil { 271 err = e 272 } 273 } 274 }() 275 276 lockStdFileDescriptorsSwapping.Lock() 277 278 if e := syscall.Dup2(int(w.Fd()), syscall.Stdout); e != nil { 279 lockStdFileDescriptorsSwapping.Unlock() 280 281 return e 282 } 283 if e := syscall.Dup2(int(w.Fd()), syscall.Stderr); e != nil { 284 lockStdFileDescriptorsSwapping.Unlock() 285 286 return e 287 } 288 289 lockStdFileDescriptorsSwapping.Unlock() 290 291 wg := &sync.WaitGroup{} 292 wg.Add(1) 293 go func() { 294 defer func() { 295 wg.Done() 296 // If there is a panic in the function call, copying from "r" does not work anymore. 297 _ = recover() 298 }() 299 scanner := bufio.NewScanner(r) 300 for scanner.Scan() { 301 text := scanner.Text() 302 ch <- text 303 } 304 }() 305 306 call() 307 308 lockStdFileDescriptorsSwapping.Lock() 309 310 C.fflush(C.stdout) 311 312 err = w.Close() 313 if err != nil { 314 lockStdFileDescriptorsSwapping.Unlock() 315 316 return err 317 } 318 w = nil 319 320 if e := syscall.Close(syscall.Stdout); e != nil { 321 lockStdFileDescriptorsSwapping.Unlock() 322 323 return e 324 } 325 if e := syscall.Close(syscall.Stderr); e != nil { 326 lockStdFileDescriptorsSwapping.Unlock() 327 328 return e 329 } 330 331 lockStdFileDescriptorsSwapping.Unlock() 332 333 wg.Wait() 334 return err 335 } 336 337 type Buffer struct { 338 rd *io.PipeReader 339 wr *io.PipeWriter 340 } 341 342 func NewBuffer() *Buffer { 343 rd, wr := io.Pipe() 344 return &Buffer{rd, wr} 345 } 346 347 func (b *Buffer) Close() error { 348 if b.rd == nil { 349 return nil 350 } 351 _ = b.rd.Close() 352 _ = b.wr.Close() 353 b.rd = nil 354 b.wr = nil 355 return nil 356 } 357 358 func (b *Buffer) Read(d []byte) (int, error) { 359 return b.rd.Read(d) 360 } 361 362 func (b *Buffer) Write(d []byte) (int, error) { 363 return b.wr.Write(d) 364 }