seccomp-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 40f614938f158e9d50bae1663c7a70e706e90e3708c40aad4fe104f51c7e580b
4
+ data.tar.gz: b31a0b00fe82b56887508fdd4d7d87e7fdcb71de4f5751f7d783b1ab5126786d
5
+ SHA512:
6
+ metadata.gz: 0a44ae3091f60578544fa965c9ab93f52c29d8e0294fc115cd010028a55d62b4f8e72078413c951ee9116757f3a9720357ba9c6b5b596f4a901044daf888fa46
7
+ data.tar.gz: 0beda9a0f882861e3941cdff169695d951a132a4ad54a360e1f104e4a33b7fc12192888890ca17dc0feebeb83fdf0a16e8fd503233863615d01051ce2f93eb05
data/.rubocop.yml ADDED
@@ -0,0 +1,43 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.1
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 100
8
+
9
+ Metrics/ClassLength:
10
+ Max: 250
11
+
12
+ Metrics/MethodLength:
13
+ Max: 30
14
+
15
+ Style/Documentation:
16
+ Enabled: false
17
+
18
+ Style/StringLiterals:
19
+ EnforcedStyle: double_quotes
20
+
21
+ Gemspec/DevelopmentDependencies:
22
+ Enabled: false
23
+
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - "spec/**/*"
27
+ - "*.gemspec"
28
+
29
+ Metrics/AbcSize:
30
+ Exclude:
31
+ - "spec/support/**/*"
32
+
33
+ Naming/MethodParameterName:
34
+ AllowedNames:
35
+ - fd
36
+ - id
37
+ - io
38
+ - op
39
+ - val
40
+
41
+ Security/MarshalLoad:
42
+ Exclude:
43
+ - "spec/live/**/*"
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --markup markdown
2
+ --no-private
3
+ lib/**/*.rb
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # seccomp-ruby
2
+
3
+ Native CRuby bindings for [libseccomp](https://github.com/seccomp/libseccomp). The gem exposes
4
+ the C API through `Seccomp::LowLevel` and a Ruby-friendly filter, argument DSL, transaction, and
5
+ user-notification API.
6
+
7
+ > [!WARNING]
8
+ > A loaded seccomp filter cannot be removed or relaxed before the process exits. Ruby itself uses
9
+ > many system calls; an incomplete allowlist can crash the VM immediately. Always validate a filter
10
+ > in a forked child process before using it in production.
11
+
12
+ ## Requirements
13
+
14
+ - Linux and CRuby 3.1 or newer
15
+ - libseccomp 2.5 or newer and its development headers
16
+ - A C compiler
17
+
18
+ On Debian or Ubuntu:
19
+
20
+ ```sh
21
+ sudo apt-get install libseccomp-dev build-essential
22
+ bundle install
23
+ bundle exec rake compile
24
+ ```
25
+
26
+ The gem name is currently `seccomp-ruby`:
27
+
28
+ ```ruby
29
+ gem "seccomp-ruby"
30
+ ```
31
+
32
+ ## Quick start
33
+
34
+ Construct and inspect filters in the parent process, but load them only after `fork`:
35
+
36
+ ```ruby
37
+ require "libseccomp"
38
+
39
+ filter = Seccomp.filter(default: :kill_process) do
40
+ no_new_privs true
41
+ deny :ptrace, errno: Errno::EPERM
42
+ allow :read, :write, :exit, :exit_group, :rt_sigreturn
43
+ end
44
+
45
+ puts filter.to_pfc
46
+ pid = fork do
47
+ filter.load!
48
+ # Run sandboxed work here.
49
+ Process.exit!(0)
50
+ end
51
+ Process.waitpid(pid)
52
+ filter.close
53
+ ```
54
+
55
+ Argument comparisons use explicit keywords for masked equality:
56
+
57
+ ```ruby
58
+ filter.allow(:openat, filter.arg(2).masked_eq(mask: 0o3, value: 0))
59
+ filter.deny(:close, filter.arg(0).eq(2), errno: Errno::EBADF)
60
+ ```
61
+
62
+ Use `Seccomp.supports?` before calling optional APIs. It checks both compile-time availability and
63
+ the runtime API level where applicable:
64
+
65
+ ```ruby
66
+ filter.transaction { filter.allow(:read) } if Seccomp.supports?(:transaction)
67
+ filter.precompute if Seccomp.supports?(:precompute)
68
+ notification.add_fd(file) if Seccomp.supports?(:notify_addfd)
69
+ ```
70
+
71
+ ## User notifications
72
+
73
+ `Seccomp::Notifier#receive` integrates with Ruby IO waiting and accepts a timeout. Notification
74
+ errors use normal positive errno values at the Ruby boundary:
75
+
76
+ ```ruby
77
+ notification.deny(Errno::EPERM) # converted to the kernel's negative errno convention
78
+ notification.respond(error: Errno::EPERM) # direct responses use the same conversion
79
+ notification.continue!
80
+ ```
81
+
82
+ > [!CAUTION]
83
+ > Notification arguments may contain pointers into another process. If you read through
84
+ > `/proc/<pid>/mem`, call `notification.valid?` again after reading and before responding. This
85
+ > reduces, but cannot eliminate, time-of-check/time-of-use races. `continue!` must only be used when
86
+ > the target syscall is safe to execute with its current arguments.
87
+
88
+ libseccomp keeps one notification listener in process-global state. Closing its `Notifier` or
89
+ owning `Filter` closes and resets that listener. A second notification filter is rejected while
90
+ the listener is active; use one notification supervisor per process.
91
+
92
+ > [!WARNING]
93
+ > A loaded notification filter must allow `close(2)` while its listener is being released. A rule
94
+ > that denies or kills `close(2)` can leave the listener open, and a notification rule for
95
+ > `close(2)` requires a supervisor to answer during cleanup. This cannot be repaired after the
96
+ > irreversible filter is loaded; keep the supervisor running or end the sandbox with
97
+ > `Process.exit!`.
98
+
99
+ If file descriptor 0 is closed when a notification filter is loaded, the gem reserves it with
100
+ `/dev/null` and keeps it open after a successful load; closing it afterward could itself be blocked
101
+ by the irreversible filter. Filters without notification actions leave file descriptor 0 unchanged.
102
+ If a notification load fails after an earlier filter was loaded, the reservation is kept rather than
103
+ risk an internal `close(2)` being intercepted. Rolling back a transaction that added a notification
104
+ rule requires `Filter#reset` or a new notification rule before loading that filter, due to native
105
+ libseccomp state retained by the rejected transaction.
106
+
107
+ ## Thread and architecture notes
108
+
109
+ - `tsync` applies a filter to every thread. Ruby VM threads require syscalls such as `futex`,
110
+ `rt_sigprocmask`, and `membarrier`; blocking them may terminate or deadlock the VM.
111
+ - `width: 32` masks comparison values to 32 bits. On 32-bit ABIs, upper halves of 64-bit syscall
112
+ arguments may not be meaningful.
113
+ - x32 and x86_64 are distinct architecture tokens.
114
+ - `Seccomp.api_level=` changes process-global libseccomp state and should be used once at startup.
115
+
116
+ ## Development
117
+
118
+ ```sh
119
+ bundle exec rake compile
120
+ bundle exec rake spec:unit
121
+ bundle exec rake spec:live # all load! calls are fork-isolated
122
+ bundle exec rubocop
123
+ bundle exec rbs validate
124
+ bundle exec yard stats --list-undoc
125
+ ```
126
+
127
+ The live suite must run on Linux. In a container, use `--security-opt seccomp=unconfined`.
128
+
129
+ ## License
130
+
131
+ This project retains its existing MIT license. It dynamically links to libseccomp, which is
132
+ licensed under LGPL-2.1.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/extensiontask"
5
+ require "rspec/core/rake_task"
6
+
7
+ Rake::ExtensionTask.new("seccomp_ext")
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |task|
10
+ task.pattern = "spec/{unit,live}/**/*_spec.rb"
11
+ end
12
+
13
+ RSpec::Core::RakeTask.new("spec:unit") do |task|
14
+ task.pattern = "spec/unit/**/*_spec.rb"
15
+ end
16
+
17
+ RSpec::Core::RakeTask.new("spec:live") do |task|
18
+ task.pattern = "spec/live/**/*_spec.rb"
19
+ end
20
+
21
+ require "rubocop/rake_task"
22
+ RuboCop::RakeTask.new
23
+
24
+ desc "Validate RBS signatures"
25
+ task :rbs do
26
+ sh "bundle exec rbs validate"
27
+ end
28
+
29
+ desc "Report API documentation coverage"
30
+ task :yard do
31
+ sh "bundle exec yard stats --list-undoc"
32
+ end
33
+
34
+ task default: %i[compile spec rubocop]
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING: load! is irreversible. This example loads only in a forked child.
4
+ require "libseccomp"
5
+
6
+ pid = fork do
7
+ filter = Seccomp::Filter.new(:allow)
8
+ filter.deny(:write, filter.arg(0).eq($stdout.fileno), errno: Errno::EPERM)
9
+ filter.load!
10
+ begin
11
+ $stdout.syswrite("blocked\n")
12
+ rescue Errno::EPERM
13
+ $stderr.syswrite("writes to stdout are blocked\n")
14
+ end
15
+ Process.exit!(0)
16
+ end
17
+ _, status = Process.waitpid2(pid)
18
+ abort "sandbox child failed" unless status.success?
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING: load! is irreversible. This example loads only in a forked child.
4
+ require "libseccomp"
5
+
6
+ required = %i[
7
+ read write close exit exit_group rt_sigreturn rt_sigaction rt_sigprocmask futex mmap munmap
8
+ mprotect madvise brk clock_gettime getpid gettid sigaltstack fstat newfstatat getrandom
9
+ ]
10
+ filter = Seccomp::Filter.new(:kill_process)
11
+ required.each { |syscall| filter.allow(syscall) }
12
+
13
+ pid = fork do
14
+ filter.load!
15
+ $stdout.syswrite("sandbox active\n")
16
+ Process.exit!(0)
17
+ end
18
+ _, status = Process.waitpid2(pid)
19
+ filter.close
20
+ abort "sandbox child failed" unless status.success?
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING: load! is irreversible. This example loads only in a forked child.
4
+ require "libseccomp"
5
+
6
+ pid = fork do
7
+ filter = Seccomp::Filter.new(:allow)
8
+ filter.deny(:openat, errno: Errno::EPERM)
9
+ filter.load!
10
+ begin
11
+ File.read("/etc/hostname")
12
+ rescue Errno::EPERM
13
+ $stdout.syswrite("openat denied with EPERM\n")
14
+ end
15
+ Process.exit!(0)
16
+ end
17
+ _, status = Process.waitpid2(pid)
18
+ abort "sandbox child failed" unless status.success?
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING: load! is irreversible. This example loads only in a forked child.
4
+ # Pointer arguments must be read before valid? is rechecked to reduce TOCTOU risk.
5
+ require "fiddle"
6
+ require "libseccomp"
7
+
8
+ pid = fork do
9
+ queue = Queue.new
10
+ supervisor = Thread.new do
11
+ notification = queue.pop.receive(timeout: 5)
12
+ notification.valid? ? notification.continue! : nil
13
+ end
14
+ filter = Seccomp::Filter.new(:allow)
15
+ filter.notify(:getppid)
16
+ filter.load!
17
+ queue << filter.notifier
18
+
19
+ syscall = Fiddle::Function.new(Fiddle::Handle::DEFAULT["syscall"],
20
+ [Fiddle::TYPE_LONG], Fiddle::TYPE_LONG)
21
+ syscall.call(Seccomp::Syscall.number(:getppid))
22
+ supervisor.join
23
+ Process.exit!(0)
24
+ end
25
+ _, status = Process.waitpid2(pid)
26
+ abort "sandbox child failed" unless status.success?
@@ -0,0 +1,133 @@
1
+ #include "seccomp_ext.h"
2
+
3
+ #define CONST(module, name, value) rb_define_const((module), (name), seccomp_ext_uint32(value))
4
+
5
+ void
6
+ seccomp_ext_init_constants(void)
7
+ {
8
+ VALUE action = rb_define_module_under(mSeccomp, "Action");
9
+ VALUE arch = rb_define_module_under(mSeccomp, "Arch");
10
+ VALUE compare = rb_define_module_under(mSeccomp, "Compare");
11
+ VALUE attribute = rb_define_module_under(mSeccomp, "FilterAttr");
12
+
13
+ CONST(action, "KILL_PROCESS", SCMP_ACT_KILL_PROCESS);
14
+ CONST(action, "KILL_THREAD", SCMP_ACT_KILL_THREAD);
15
+ CONST(action, "KILL", SCMP_ACT_KILL);
16
+ CONST(action, "TRAP", SCMP_ACT_TRAP);
17
+ CONST(action, "NOTIFY", SCMP_ACT_NOTIFY);
18
+ CONST(action, "LOG", SCMP_ACT_LOG);
19
+ CONST(action, "ALLOW", SCMP_ACT_ALLOW);
20
+ #ifdef HAVE_SCMP_ACT_TRAPX
21
+ CONST(action, "TRAPX", SCMP_ACT_TRAPX(0));
22
+ #endif
23
+
24
+ #ifdef SCMP_ARCH_NATIVE
25
+ CONST(arch, "NATIVE", SCMP_ARCH_NATIVE);
26
+ #endif
27
+ #ifdef SCMP_ARCH_X86
28
+ CONST(arch, "X86", SCMP_ARCH_X86);
29
+ #endif
30
+ #ifdef SCMP_ARCH_X86_64
31
+ CONST(arch, "X86_64", SCMP_ARCH_X86_64);
32
+ #endif
33
+ #ifdef SCMP_ARCH_X32
34
+ CONST(arch, "X32", SCMP_ARCH_X32);
35
+ #endif
36
+ #ifdef SCMP_ARCH_ARM
37
+ CONST(arch, "ARM", SCMP_ARCH_ARM);
38
+ #endif
39
+ #ifdef SCMP_ARCH_AARCH64
40
+ CONST(arch, "AARCH64", SCMP_ARCH_AARCH64);
41
+ #endif
42
+ #ifdef SCMP_ARCH_LOONGARCH64
43
+ CONST(arch, "LOONGARCH64", SCMP_ARCH_LOONGARCH64);
44
+ #endif
45
+ #ifdef SCMP_ARCH_M68K
46
+ CONST(arch, "M68K", SCMP_ARCH_M68K);
47
+ #endif
48
+ #ifdef SCMP_ARCH_MIPS
49
+ CONST(arch, "MIPS", SCMP_ARCH_MIPS);
50
+ #endif
51
+ #ifdef SCMP_ARCH_MIPS64
52
+ CONST(arch, "MIPS64", SCMP_ARCH_MIPS64);
53
+ #endif
54
+ #ifdef SCMP_ARCH_MIPS64N32
55
+ CONST(arch, "MIPS64N32", SCMP_ARCH_MIPS64N32);
56
+ #endif
57
+ #ifdef SCMP_ARCH_MIPSEL
58
+ CONST(arch, "MIPSEL", SCMP_ARCH_MIPSEL);
59
+ #endif
60
+ #ifdef SCMP_ARCH_MIPSEL64
61
+ CONST(arch, "MIPSEL64", SCMP_ARCH_MIPSEL64);
62
+ #endif
63
+ #ifdef SCMP_ARCH_MIPSEL64N32
64
+ CONST(arch, "MIPSEL64N32", SCMP_ARCH_MIPSEL64N32);
65
+ #endif
66
+ #ifdef SCMP_ARCH_PPC
67
+ CONST(arch, "PPC", SCMP_ARCH_PPC);
68
+ #endif
69
+ #ifdef SCMP_ARCH_PPC64
70
+ CONST(arch, "PPC64", SCMP_ARCH_PPC64);
71
+ #endif
72
+ #ifdef SCMP_ARCH_PPC64LE
73
+ CONST(arch, "PPC64LE", SCMP_ARCH_PPC64LE);
74
+ #endif
75
+ #ifdef SCMP_ARCH_S390
76
+ CONST(arch, "S390", SCMP_ARCH_S390);
77
+ #endif
78
+ #ifdef SCMP_ARCH_S390X
79
+ CONST(arch, "S390X", SCMP_ARCH_S390X);
80
+ #endif
81
+ #ifdef SCMP_ARCH_PARISC
82
+ CONST(arch, "PARISC", SCMP_ARCH_PARISC);
83
+ #endif
84
+ #ifdef SCMP_ARCH_PARISC64
85
+ CONST(arch, "PARISC64", SCMP_ARCH_PARISC64);
86
+ #endif
87
+ #ifdef SCMP_ARCH_RISCV64
88
+ CONST(arch, "RISCV64", SCMP_ARCH_RISCV64);
89
+ #endif
90
+ #ifdef SCMP_ARCH_SH
91
+ CONST(arch, "SH", SCMP_ARCH_SH);
92
+ #endif
93
+ #ifdef SCMP_ARCH_SHEB
94
+ CONST(arch, "SHEB", SCMP_ARCH_SHEB);
95
+ #endif
96
+
97
+ CONST(compare, "NE", SCMP_CMP_NE);
98
+ CONST(compare, "LT", SCMP_CMP_LT);
99
+ CONST(compare, "LE", SCMP_CMP_LE);
100
+ CONST(compare, "EQ", SCMP_CMP_EQ);
101
+ CONST(compare, "GE", SCMP_CMP_GE);
102
+ CONST(compare, "GT", SCMP_CMP_GT);
103
+ CONST(compare, "MASKED_EQ", SCMP_CMP_MASKED_EQ);
104
+
105
+ CONST(attribute, "ACT_DEFAULT", SCMP_FLTATR_ACT_DEFAULT);
106
+ CONST(attribute, "ACT_BADARCH", SCMP_FLTATR_ACT_BADARCH);
107
+ CONST(attribute, "CTL_NNP", SCMP_FLTATR_CTL_NNP);
108
+ CONST(attribute, "CTL_TSYNC", SCMP_FLTATR_CTL_TSYNC);
109
+ CONST(attribute, "API_TSKIP", SCMP_FLTATR_API_TSKIP);
110
+ CONST(attribute, "CTL_LOG", SCMP_FLTATR_CTL_LOG);
111
+ CONST(attribute, "CTL_SSB", SCMP_FLTATR_CTL_SSB);
112
+ #ifdef HAVE_CONST_SCMP_FLTATR_CTL_OPTIMIZE
113
+ CONST(attribute, "CTL_OPTIMIZE", SCMP_FLTATR_CTL_OPTIMIZE);
114
+ #endif
115
+ #ifdef HAVE_CONST_SCMP_FLTATR_API_SYSRAWRC
116
+ CONST(attribute, "API_SYSRAWRC", SCMP_FLTATR_API_SYSRAWRC);
117
+ #endif
118
+ #ifdef HAVE_CONST_SCMP_FLTATR_CTL_WAITKILL
119
+ CONST(attribute, "CTL_WAITKILL", SCMP_FLTATR_CTL_WAITKILL);
120
+ #endif
121
+
122
+ rb_define_const(mSeccompLowLevel, "NR_SCMP_ERROR", INT2NUM(__NR_SCMP_ERROR));
123
+ rb_define_const(mSeccompLowLevel, "NR_SCMP_UNDEF", INT2NUM(__NR_SCMP_UNDEF));
124
+ #ifdef SECCOMP_USER_NOTIF_FLAG_CONTINUE
125
+ CONST(mSeccompLowLevel, "USER_NOTIF_FLAG_CONTINUE", SECCOMP_USER_NOTIF_FLAG_CONTINUE);
126
+ #endif
127
+ #ifdef SECCOMP_ADDFD_FLAG_SETFD
128
+ CONST(mSeccompLowLevel, "ADDFD_FLAG_SETFD", SECCOMP_ADDFD_FLAG_SETFD);
129
+ #endif
130
+ #ifdef SECCOMP_ADDFD_FLAG_SEND
131
+ CONST(mSeccompLowLevel, "ADDFD_FLAG_SEND", SECCOMP_ADDFD_FLAG_SEND);
132
+ #endif
133
+ }
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ abort "libseccomp is Linux-only" unless RUBY_PLATFORM.include?("linux")
6
+ abort "seccomp.h is required" unless have_header("seccomp.h")
7
+ abort "libseccomp is required" unless have_library("seccomp", "seccomp_init")
8
+ abort "libseccomp >= 2.5 is required" unless have_func("seccomp_notify_alloc", "seccomp.h")
9
+
10
+ %w[
11
+ seccomp_notify_addfd
12
+ seccomp_transaction_start
13
+ seccomp_transaction_commit
14
+ seccomp_transaction_reject
15
+ seccomp_precompute
16
+ seccomp_export_bpf_mem
17
+ ].each { |function| have_func(function, "seccomp.h") }
18
+
19
+ if have_macro("SECCOMP_IOCTL_NOTIF_ADDFD", %w[seccomp.h sys/ioctl.h])
20
+ # mkmf exposes extra compiler definitions through this global.
21
+ # rubocop:disable-next Style/GlobalVars
22
+ $defs << "-DHAVE_SECCOMP_IOCTL_NOTIF_ADDFD"
23
+ end
24
+
25
+ %w[
26
+ SCMP_ACT_TRAPX
27
+ SCMP_ARCH_LOONGARCH64
28
+ SCMP_ARCH_M68K
29
+ SCMP_ARCH_SH
30
+ SCMP_ARCH_SHEB
31
+ SCMP_ARCH_RISCV64
32
+ ].each do |macro|
33
+ # mkmf exposes extra compiler definitions through this global.
34
+ # rubocop:disable-next Style/GlobalVars
35
+ $defs << "-DHAVE_#{macro}" if have_macro(macro, "seccomp.h")
36
+ end
37
+
38
+ %w[
39
+ SCMP_FLTATR_CTL_WAITKILL
40
+ SCMP_FLTATR_API_SYSRAWRC
41
+ SCMP_FLTATR_CTL_OPTIMIZE
42
+ ].each { |constant| have_const(constant, "seccomp.h") }
43
+
44
+ have_struct_member("struct seccomp_notif_addfd", "newfd_flags", "seccomp.h")
45
+ create_makefile("seccomp_ext")