Start receives and handles intercepted syscalls until all processes exit.
()
| 288 | |
| 289 | // Start receives and handles intercepted syscalls until all processes exit. |
| 290 | func (e *Engine) Start() { |
| 291 | N := runtime.NumCPU() |
| 292 | |
| 293 | var wg sync.WaitGroup |
| 294 | slog.Debug("starting parallel receive-dispatch-handle loop", "workers", N) |
| 295 | defer slog.Debug("finished parallel receive-dispatch-handle loop") |
| 296 | |
| 297 | failed := make(chan *seccomp.Notif, N) |
| 298 | ch := make(chan *seccomp.Notif, N) |
| 299 | for i := 0; i < N; i++ { |
| 300 | wg.Add(1) |
| 301 | go func() { |
| 302 | defer wg.Done() |
| 303 | |
| 304 | // TODO: sched_setaffinity to lock to CPU here? It'd be nice to have the |
| 305 | // system call handler run on the same CPU as the tracee process that is |
| 306 | // executing the system call. |
| 307 | runtime.LockOSThread() |
| 308 | defer runtime.UnlockOSThread() |
| 309 | |
| 310 | defer e.panicGuard(ch, failed) |
| 311 | |
| 312 | var pending *seccomp.Notif |
| 313 | defer func() { |
| 314 | if pending != nil { |
| 315 | failed <- pending |
| 316 | } |
| 317 | }() |
| 318 | |
| 319 | for n := range ch { |
| 320 | if n == nil { |
| 321 | break |
| 322 | } |
| 323 | |
| 324 | pending = n |
| 325 | if e.inPanic.Load() { |
| 326 | return |
| 327 | } |
| 328 | e.handle(n) |
| 329 | pending = nil |
| 330 | } |
| 331 | }() |
| 332 | } |
| 333 | |
| 334 | dispatch: |
| 335 | for e.countRunning() > 0 { |
| 336 | n, errno := e.seccomp.Receive() |
| 337 | switch errno { |
| 338 | case 0: |
| 339 | ch <- n |
| 340 | case unix.ENOENT: |
| 341 | // The target was killed by a signal or its syscall was interrupted by a |
| 342 | // signal handler. |
| 343 | continue |
| 344 | case unix.EBADF: |
| 345 | // The seccomp listener file descriptor was closed. |
| 346 | break dispatch |
| 347 | default: |
no test coverage detected