package seccomp import ( "errors" "io" "io/fs" "os" "runtime" "sync" "sync/atomic" "syscall" ) // Export returns a temporary file containing a bpf program emitted by libseccomp. func Export(opts SyscallOpts) (f *os.File, err error) { if f, err = tmpfile(); err != nil { return } if err = exportFilter(f.Fd(), opts); err != nil { return } _, err = f.Seek(0, io.SeekStart) return } /* An Exporter writes a BPF program to an output stream. Methods of Exporter are not safe for concurrent use. An Exporter must not be copied after first use. */ type Exporter struct { opts SyscallOpts r, w *os.File prepareOnce sync.Once prepareErr error closeErr atomic.Pointer[error] exportErr <-chan error } func (e *Exporter) closeWrite() error { if !e.closeErr.CompareAndSwap(nil, &fs.ErrInvalid) { return *e.closeErr.Load() } err := e.w.Close() e.closeErr.Store(&err) return err } func (e *Exporter) prepare() error { e.prepareOnce.Do(func() { if r, w, err := os.Pipe(); err != nil { e.prepareErr = err return } else { e.r, e.w = r, w } ec := make(chan error, 1) go func() { ec <- exportFilter(e.w.Fd(), e.opts); close(ec); _ = e.closeWrite() }() e.exportErr = ec runtime.SetFinalizer(e, (*Exporter).Close) }) return e.prepareErr } func (e *Exporter) Read(p []byte) (n int, err error) { if err = e.prepare(); err != nil { return } return e.r.Read(p) } func (e *Exporter) Close() error { if e.r == nil { return syscall.EINVAL } runtime.SetFinalizer(e, nil) // this hangs if the cgo thread fails to exit return errors.Join(e.closeWrite(), <-e.exportErr) } func New(opts SyscallOpts) *Exporter { return &Exporter{opts: opts} }