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.
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seccomp
4
+ # One immutable user-notification request.
5
+ class Notification
6
+ # Fields copied from the kernel notification request.
7
+ # @api private
8
+ ATTRIBUTES = %i[id pid flags syscall arch instruction_pointer args].freeze
9
+ attr_reader :id, :pid, :flags, :syscall, :arch, :instruction_pointer, :args
10
+
11
+ # @param notifier [Notifier] response channel
12
+ # @param data [Hash] low-level request fields
13
+ # @return [void]
14
+ # @example `Notification.new(notifier, data)`
15
+ def initialize(notifier, data)
16
+ @notifier = notifier
17
+ ATTRIBUTES.each { |attribute| instance_variable_set("@#{attribute}", data.fetch(attribute)) }
18
+ @args.freeze
19
+ end
20
+
21
+ # @return [Boolean] whether the kernel still recognizes the request
22
+ # @raise [Error] if validation fails
23
+ # @example `notification.valid?`
24
+ def valid?
25
+ @notifier.id_valid?(id)
26
+ end
27
+
28
+ # @param val [Integer] syscall return value
29
+ # @param error [Integer, SystemCallError, Class] positive or negative errno
30
+ # @param flags [Integer] response flags
31
+ # @return [nil]
32
+ # @raise [Error] if responding fails
33
+ # @example `notification.respond(val: 3)`
34
+ def respond(val: 0, error: 0, flags: 0)
35
+ error = -errno_number(error).abs unless error.is_a?(Integer) && error.zero?
36
+ @notifier.respond(id: id, val: val, error: error, flags: flags)
37
+ end
38
+
39
+ # @param value [Integer] syscall return value
40
+ # @return [nil]
41
+ # @raise [Error] if responding fails
42
+ # @example `notification.allow(0)`
43
+ def allow(value = 0)
44
+ respond(val: value)
45
+ end
46
+
47
+ # @param errno [Integer, SystemCallError, Class] positive errno
48
+ # @return [nil]
49
+ # @raise [Error] if responding fails
50
+ # @raise [ArgumentError] for an invalid errno
51
+ # @example `notification.deny(Errno::EPERM)`
52
+ def deny(errno)
53
+ number = errno_number(errno)
54
+ raise ArgumentError, "errno must not be zero" if number.zero?
55
+
56
+ respond(error: -number.abs)
57
+ end
58
+
59
+ # @return [nil]
60
+ # @raise [Error] if responding fails
61
+ # @example `notification.continue!`
62
+ def continue!
63
+ respond(flags: LowLevel::USER_NOTIF_FLAG_CONTINUE)
64
+ end
65
+
66
+ # @param io [IO] descriptor to install
67
+ # @param flags [Integer] addfd flags
68
+ # @param newfd [Integer] requested target descriptor
69
+ # @param newfd_flags [Integer] target descriptor flags
70
+ # @return [Integer] installed descriptor
71
+ # @raise [NotSupportedError] when unavailable
72
+ # @example `notification.add_fd(file)`
73
+ def add_fd(io, flags: 0, newfd: 0, newfd_flags: 0)
74
+ @notifier.add_fd(id: id, source_fd: io.fileno, flags: flags,
75
+ newfd: newfd, newfd_flags: newfd_flags)
76
+ end
77
+
78
+ private
79
+
80
+ def errno_number(value)
81
+ number = if value.is_a?(Class) && value < SystemCallError
82
+ value.const_get(:Errno, false) if value.const_defined?(:Errno, false)
83
+ elsif value.is_a?(SystemCallError)
84
+ value.errno
85
+ else
86
+ value
87
+ end
88
+ unless number.is_a?(Integer)
89
+ raise ArgumentError, "errno must be an Integer or system error with an errno"
90
+ end
91
+
92
+ number
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "io/wait"
4
+
5
+ module Seccomp
6
+ # Receives and responds to seccomp user notifications.
7
+ class Notifier
8
+ include Enumerable
9
+
10
+ # ponytail: bounded polling avoids a native wakeup channel; replace only if idle cost matters.
11
+ CLOSE_POLL_INTERVAL = 0.1
12
+ private_constant :CLOSE_POLL_INTERVAL
13
+
14
+ # @param filter [Filter] loaded owner filter
15
+ # @param fd [Integer] notification listener descriptor
16
+ # @return [void]
17
+ # @example `Notifier.new(filter, fd)`
18
+ def initialize(filter, fd)
19
+ @filter = filter
20
+ @fd = fd
21
+ @io = IO.for_fd(fd, autoclose: false)
22
+ @receive_lock = filter.__send__(:notification_receive_lock)
23
+ end
24
+
25
+ # @param timeout [Numeric, nil] nonnegative finite real seconds to wait
26
+ # @return [Notification, nil] request or nil on timeout
27
+ # @raise [TypeError, ArgumentError, RangeError] if timeout is invalid
28
+ # @raise [ClosedFilterError] if the owning filter closes
29
+ # @raise [NotificationCanceledError] if the target exits
30
+ # @example `notifier.receive(timeout: 1)`
31
+ def receive(timeout: nil)
32
+ validate_timeout!(timeout)
33
+ deadline = monotonic_time + timeout if timeout
34
+ acquired = acquire_receive_lock(deadline)
35
+ return nil unless acquired
36
+
37
+ receive_locked(remaining_timeout(deadline))
38
+ ensure
39
+ @receive_lock.unlock if acquired
40
+ end
41
+
42
+ # @yield [Notification] each request
43
+ # @return [Enumerator, Notifier] enumerator or receiver
44
+ # @raise [Error] if receiving fails
45
+ # @example `notifier.each { |notification| notification.continue! }`
46
+ def each
47
+ return enum_for(__method__) unless block_given?
48
+
49
+ loop do
50
+ yield receive
51
+ rescue NotificationCanceledError
52
+ next
53
+ end
54
+ end
55
+
56
+ # @param id [Integer] notification ID
57
+ # @param val [Integer] syscall return value
58
+ # @param error [Integer] kernel-style negative errno
59
+ # @param flags [Integer] response flags
60
+ # @return [nil]
61
+ # @raise [Error] if responding fails
62
+ # @example `notifier.respond(id: id, val: 0)`
63
+ def respond(id:, val: 0, error: 0, flags: 0)
64
+ ensure_open!
65
+ check_notification_result!(LowLevel.notify_respond(@fd, id, val, error, flags),
66
+ call: "seccomp_notify_respond")
67
+ nil
68
+ end
69
+
70
+ # @param id [Integer] notification ID
71
+ # @return [Boolean] whether still valid
72
+ # @raise [Error] if validation fails
73
+ # @example `notifier.id_valid?(id)`
74
+ def id_valid?(id)
75
+ ensure_open!
76
+ result = LowLevel.notify_id_valid(@fd, id)
77
+ return true if result.zero?
78
+
79
+ ensure_open!
80
+ return false if [-Errno::ENOENT::Errno, -Errno::EINVAL::Errno].include?(result)
81
+
82
+ check_notification_result!(result, call: "seccomp_notify_id_valid")
83
+ end
84
+
85
+ # @param id [Integer] notification ID
86
+ # @param source_fd [Integer] local descriptor
87
+ # @param flags [Integer] addfd flags
88
+ # @param newfd [Integer] requested target descriptor
89
+ # @param newfd_flags [Integer] target descriptor flags
90
+ # @return [Integer] installed descriptor
91
+ # @raise [NotSupportedError] when unavailable
92
+ # @example `notifier.add_fd(id: id, source_fd: file.fileno)`
93
+ def add_fd(id:, source_fd:, flags: 0, newfd: 0, newfd_flags: 0)
94
+ ensure_open!
95
+ result = LowLevel.notify_addfd(@fd, id, flags, source_fd, newfd, newfd_flags)
96
+ check_notification_result!(result, call: "seccomp_notify_addfd")
97
+ end
98
+
99
+ # @return [nil]
100
+ # @example `notifier.close`
101
+ def close
102
+ @filter.close
103
+ end
104
+
105
+ # @return [Boolean] whether the owner filter is closed
106
+ # @example `notifier.closed?`
107
+ def closed?
108
+ @filter.closed?
109
+ end
110
+
111
+ private
112
+
113
+ def receive_locked(timeout)
114
+ return nil unless wait_readable(timeout)
115
+
116
+ ensure_open!
117
+
118
+ result, data = LowLevel.notify_receive(@fd)
119
+ if [-Errno::ENOENT::Errno, -Errno::ECANCELED::Errno].include?(result)
120
+ ensure_open!
121
+ raise NotificationCanceledError.new(
122
+ "seccomp_notify_receive failed: notification was canceled",
123
+ errno: -result, call: "seccomp_notify_receive"
124
+ )
125
+ end
126
+
127
+ check_notification_result!(result, call: "seccomp_notify_receive")
128
+ Notification.new(self, data)
129
+ end
130
+
131
+ def acquire_receive_lock(deadline)
132
+ return @receive_lock.lock unless deadline
133
+
134
+ loop do
135
+ return true if @receive_lock.try_lock
136
+
137
+ remaining = deadline - monotonic_time
138
+ return false if remaining <= 0
139
+
140
+ sleep([remaining, CLOSE_POLL_INTERVAL].min)
141
+ end
142
+ end
143
+
144
+ def remaining_timeout(deadline)
145
+ deadline && [deadline - monotonic_time, 0].max
146
+ end
147
+
148
+ def monotonic_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
149
+
150
+ def validate_timeout!(timeout)
151
+ return if timeout.nil?
152
+ unless timeout.is_a?(Numeric) && timeout.real?
153
+ raise TypeError, "timeout must be a real Numeric or nil"
154
+ end
155
+ raise ArgumentError, "time interval must not be negative" if timeout.negative?
156
+ raise RangeError, "time interval must be finite" unless timeout.finite?
157
+ end
158
+
159
+ def check_notification_result!(result, call:)
160
+ ensure_open! if result.negative?
161
+ Error.check!(result, call: call)
162
+ end
163
+
164
+ def wait_readable(timeout)
165
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout if timeout
166
+ remaining = timeout
167
+ loop do
168
+ ensure_open!
169
+ return true if @io.wait_readable(poll_timeout(remaining))
170
+
171
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) if deadline
172
+ return false if remaining && remaining <= 0
173
+ end
174
+ rescue Errno::EBADF
175
+ ensure_open!
176
+ raise
177
+ end
178
+
179
+ def poll_timeout(remaining)
180
+ [remaining || CLOSE_POLL_INTERVAL, CLOSE_POLL_INTERVAL].min
181
+ end
182
+
183
+ def ensure_open!
184
+ raise ClosedFilterError, "notification filter is closed" if closed?
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seccomp
4
+ # Syscall name and number resolution.
5
+ module Syscall
6
+ module_function
7
+
8
+ # @param value [Symbol, String, Integer] syscall
9
+ # @param arch [Symbol, String, Integer, nil] target architecture
10
+ # @param rewrite [Boolean] rewrite multiplexed syscalls
11
+ # @return [Integer] syscall number
12
+ # @raise [UnknownSyscallError] for an unknown name
13
+ # @example `Syscall.number(:write, arch: :x86_64)`
14
+ def number(value, arch: nil, rewrite: false)
15
+ return value if value.is_a?(Integer)
16
+
17
+ name = value.to_s
18
+ result = if arch
19
+ token = Arch.resolve(arch)
20
+ if rewrite
21
+ LowLevel.syscall_resolve_name_rewrite(token, name)
22
+ else
23
+ LowLevel.syscall_resolve_name_arch(token, name)
24
+ end
25
+ else
26
+ LowLevel.syscall_resolve_name(name)
27
+ end
28
+ return result unless result == LowLevel::NR_SCMP_ERROR
29
+
30
+ raise UnknownSyscallError, "unknown syscall: #{value.inspect}"
31
+ end
32
+
33
+ # @param number [Integer] syscall number
34
+ # @param arch [Symbol, String, Integer] target architecture
35
+ # @return [String, nil] syscall name
36
+ # @raise [ArgumentError] unless number is an Integer
37
+ # @example `Syscall.name(1, arch: :x86_64)`
38
+ def name(number, arch: Arch.native)
39
+ raise ArgumentError, "syscall number must be an Integer" unless number.is_a?(Integer)
40
+
41
+ LowLevel.syscall_resolve_num_arch(Arch.resolve(arch), number)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seccomp
4
+ # Gem version.
5
+ VERSION = "0.1.0"
6
+ end
data/lib/seccomp.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "libseccomp"
data/sig/seccomp.rbs ADDED
@@ -0,0 +1,300 @@
1
+ module Seccomp
2
+ VERSION: String
3
+
4
+ type errno = Integer | SystemCallError | singleton(SystemCallError)
5
+ type action = Integer | Symbol | String | [Symbol, untyped]
6
+ type arch = Integer | Symbol | String
7
+ type syscall = Integer | Symbol | String
8
+
9
+ def self.library_version: () -> LibraryVersion
10
+ def self.api_level: () -> Integer
11
+ def self.api_level=: (Integer) -> Integer
12
+ def self.supports?: (Symbol) -> bool
13
+ def self.filter: (?default: action) { (Filter) -> untyped } -> Filter
14
+ | (?default: action) { () -> untyped } -> Filter
15
+ | (?default: action) -> Filter
16
+
17
+ class LibraryVersion
18
+ attr_accessor major: Integer
19
+ attr_accessor minor: Integer
20
+ attr_accessor micro: Integer
21
+ def to_s: () -> String
22
+ end
23
+
24
+ class Error < StandardError
25
+ attr_reader errno: Integer?
26
+ attr_reader call: String?
27
+ def initialize: (?String?, ?errno: Integer?, ?call: String?) -> void
28
+ def self.check!: (Integer, call: String, ?hint: String?) -> Integer
29
+ end
30
+ class PermissionError < Error
31
+ end
32
+ class KernelError < Error
33
+ end
34
+ class ArchError < Error
35
+ end
36
+ class ExistsError < Error
37
+ end
38
+ class InvalidArgumentError < Error
39
+ end
40
+ class NotFoundError < Error
41
+ end
42
+ class OutOfMemoryError < Error
43
+ end
44
+ class NotSupportedError < Error
45
+ end
46
+ class ValueRangeError < Error
47
+ end
48
+ class ThreadSyncError < Error
49
+ end
50
+ class UnknownSyscallError < Error
51
+ end
52
+ class ClosedFilterError < Error
53
+ end
54
+ class NotificationError < Error
55
+ end
56
+ class NotificationCanceledError < NotificationError
57
+ end
58
+
59
+ module LowLevel
60
+ NR_SCMP_ERROR: Integer
61
+ NR_SCMP_UNDEF: Integer
62
+ USER_NOTIF_FLAG_CONTINUE: Integer
63
+ ADDFD_FLAG_SETFD: Integer
64
+ ADDFD_FLAG_SEND: Integer
65
+
66
+ class Context
67
+ end
68
+
69
+ def self.version: () -> [Integer, Integer, Integer]
70
+ def self.api_get: () -> Integer
71
+ def self.api_set: (Integer) -> Integer
72
+ def self.supports?: (Symbol) -> bool
73
+ def self.init: (Integer) -> Context
74
+ def self.release: (Context) -> nil
75
+ def self.closed?: (Context) -> bool
76
+ def self.reset: (Context, Integer) -> Integer
77
+ def self.merge: (Context, Context) -> Integer
78
+ def self.attr_get: (Context, Integer) -> [Integer, Integer]
79
+ def self.attr_set: (Context, Integer, Integer) -> Integer
80
+ def self.arch_add: (Context, Integer) -> Integer
81
+ def self.arch_remove: (Context, Integer) -> Integer
82
+ def self.arch_exist: (Context, Integer) -> Integer
83
+ def self.arch_resolve_name: (String) -> Integer
84
+ def self.arch_native: () -> Integer
85
+ def self.syscall_resolve_name: (String) -> Integer
86
+ def self.syscall_resolve_name_arch: (Integer, String) -> Integer
87
+ def self.syscall_resolve_name_rewrite: (Integer, String) -> Integer
88
+ def self.syscall_resolve_num_arch: (Integer, Integer) -> String?
89
+ def self.rule_add_array: (Context, Integer, Integer, Array[[Integer, Integer, Integer, Integer]]) -> Integer
90
+ def self.rule_add_exact_array: (Context, Integer, Integer, Array[[Integer, Integer, Integer, Integer]]) -> Integer
91
+ def self.syscall_priority: (Context, Integer, Integer) -> Integer
92
+ def self.export_pfc: (Context, Integer) -> Integer
93
+ def self.export_bpf: (Context, Integer) -> Integer
94
+ def self.export_bpf_mem: (Context) -> [Integer, String?]
95
+ def self.precompute: (Context) -> Integer
96
+ def self.load: (Context) -> Integer
97
+ def self.loaded?: (Context) -> bool
98
+ def self.transaction_start: (Context) -> Integer
99
+ def self.transaction_commit: (Context) -> Integer
100
+ def self.transaction_reject: (Context) -> Integer
101
+ def self.notify_fd: (Context) -> Integer
102
+ def self.notify_receive: (Integer) -> [Integer, Hash[Symbol, untyped]?]
103
+ def self.notify_respond: (Integer, Integer, Integer, Integer, Integer) -> Integer
104
+ def self.notify_id_valid: (Integer, Integer) -> Integer
105
+ def self.notify_addfd: (Integer, Integer, Integer, Integer, Integer, Integer) -> Integer
106
+ end
107
+
108
+ module Action
109
+ KILL_PROCESS: Integer
110
+ KILL_THREAD: Integer
111
+ KILL: Integer
112
+ TRAP: Integer
113
+ NOTIFY: Integer
114
+ LOG: Integer
115
+ ALLOW: Integer
116
+ TRAPX: Integer
117
+
118
+ def self.errno: (errno) -> Integer
119
+ def self.trace: (Integer) -> Integer
120
+ def self.trap: (?Integer) -> Integer
121
+ def self.resolve: (action) -> Integer
122
+ def self.describe: (Integer) -> String
123
+ end
124
+
125
+ module Arch
126
+ NATIVE: Integer
127
+ X86: Integer
128
+ X86_64: Integer
129
+ X32: Integer
130
+ ARM: Integer
131
+ AARCH64: Integer
132
+ LOONGARCH64: Integer
133
+ M68K: Integer
134
+ MIPS: Integer
135
+ MIPS64: Integer
136
+ MIPS64N32: Integer
137
+ MIPSEL: Integer
138
+ MIPSEL64: Integer
139
+ MIPSEL64N32: Integer
140
+ PPC: Integer
141
+ PPC64: Integer
142
+ PPC64LE: Integer
143
+ S390: Integer
144
+ S390X: Integer
145
+ PARISC: Integer
146
+ PARISC64: Integer
147
+ RISCV64: Integer
148
+ SH: Integer
149
+ SHEB: Integer
150
+
151
+ def self.all: () -> Hash[Symbol, Integer]
152
+ def self.native: () -> Integer
153
+ def self.resolve: (arch) -> Integer
154
+ def self.name: (Integer) -> Symbol?
155
+ end
156
+
157
+ module Syscall
158
+ def self.number: (syscall, ?arch: arch?, ?rewrite: bool) -> Integer
159
+ def self.name: (Integer, ?arch: arch) -> String?
160
+ end
161
+
162
+ module Compare
163
+ NE: Integer
164
+ LT: Integer
165
+ LE: Integer
166
+ EQ: Integer
167
+ GE: Integer
168
+ GT: Integer
169
+ MASKED_EQ: Integer
170
+ end
171
+
172
+ module FilterAttr
173
+ ACT_DEFAULT: Integer
174
+ ACT_BADARCH: Integer
175
+ CTL_NNP: Integer
176
+ CTL_TSYNC: Integer
177
+ API_TSKIP: Integer
178
+ CTL_LOG: Integer
179
+ CTL_SSB: Integer
180
+ CTL_OPTIMIZE: Integer
181
+ API_SYSRAWRC: Integer
182
+ CTL_WAITKILL: Integer
183
+ end
184
+
185
+ class ArgCmp
186
+ attr_reader arg: Integer
187
+ attr_reader op: Integer
188
+ attr_reader datum_a: Integer
189
+ attr_reader datum_b: Integer
190
+ def initialize: (arg: Integer, op: Symbol, datum_a: Integer, ?datum_b: Integer, ?width: Integer) -> void
191
+ def to_a: () -> [Integer, Integer, Integer, Integer]
192
+ end
193
+
194
+ module Arg
195
+ class Reference
196
+ def initialize: (Integer) -> void
197
+ def ne: (Integer, ?width: Integer) -> ArgCmp
198
+ def lt: (Integer, ?width: Integer) -> ArgCmp
199
+ def le: (Integer, ?width: Integer) -> ArgCmp
200
+ def eq: (Integer, ?width: Integer) -> ArgCmp
201
+ def ge: (Integer, ?width: Integer) -> ArgCmp
202
+ def gt: (Integer, ?width: Integer) -> ArgCmp
203
+ def ==: (Integer, ?width: Integer) -> ArgCmp
204
+ def masked_eq: (mask: Integer, value: Integer, ?width: Integer) -> ArgCmp
205
+ end
206
+
207
+ module DSL
208
+ def arg: (Integer) -> Reference
209
+ end
210
+ end
211
+
212
+ class Filter
213
+ include Arg::DSL
214
+
215
+ def initialize: (?action) -> void
216
+ def self.open: (?action) { (Filter) -> untyped } -> untyped
217
+ | (?action) -> Filter
218
+ def close: () -> nil
219
+ def closed?: () -> bool
220
+ def reset: (?action?) -> self
221
+ def merge!: (Filter) -> self
222
+ def add_arch: (arch) -> self
223
+ def remove_arch: (arch) -> self
224
+ def arch?: (arch) -> bool
225
+ def archs: () -> Array[Symbol]
226
+ def add_rule: (action, syscall, *ArgCmp) -> self
227
+ def add_rule_exact: (action, syscall, *ArgCmp) -> self
228
+ def allow: (syscall, *(syscall | ArgCmp)) -> self
229
+ def deny: (syscall, *ArgCmp, ?errno: errno) -> self
230
+ def kill: (syscall, *ArgCmp) -> self
231
+ def log: (syscall, *ArgCmp) -> self
232
+ def notify: (syscall, *ArgCmp) -> self
233
+ def trap: (syscall, *ArgCmp) -> self
234
+ def trace: (syscall, Integer, *ArgCmp) -> self
235
+ def priority: (syscall, Integer) -> self
236
+ def []: (Symbol | Integer) -> Integer
237
+ def []=: (Symbol | Integer, Integer) -> Integer
238
+ def default_action: () -> Integer
239
+ def bad_arch_action: () -> Integer
240
+ def bad_arch_action=: (action) -> Integer
241
+ def no_new_privs?: () -> bool
242
+ def no_new_privs=: (bool) -> bool
243
+ def tsync?: () -> bool
244
+ def tsync=: (bool) -> bool
245
+ def tskip?: () -> bool
246
+ def tskip=: (bool) -> bool
247
+ def log?: () -> bool
248
+ def log=: (bool) -> bool
249
+ def ssb?: () -> bool
250
+ def ssb=: (bool) -> bool
251
+ def raw_rc?: () -> bool
252
+ def raw_rc=: (bool) -> bool
253
+ def wait_killable?: () -> bool
254
+ def wait_killable=: (bool) -> bool
255
+ def optimize: () -> Integer
256
+ def optimize=: (Integer) -> Integer
257
+ def export_pfc: (IO) -> nil
258
+ def export_bpf: (IO) -> nil
259
+ def to_pfc: () -> String
260
+ def to_bpf: () -> String
261
+ def precompute: () -> self
262
+ def load!: () -> nil
263
+ def loaded?: () -> bool
264
+ def transaction: () { () -> untyped } -> untyped
265
+ def notifier: () -> Notifier
266
+ def arch: (*arch) -> self
267
+ def no_new_privs: (bool) -> self
268
+ def tsync: (bool) -> self
269
+ end
270
+
271
+ class Notifier
272
+ include Enumerable[Notification]
273
+ def initialize: (Filter, Integer) -> void
274
+ def receive: (?timeout: Numeric?) -> Notification?
275
+ def each: () { (Notification) -> untyped } -> self
276
+ | () -> Enumerator[Notification, self]
277
+ def respond: (id: Integer, ?val: Integer, ?error: Integer, ?flags: Integer) -> nil
278
+ def id_valid?: (Integer) -> bool
279
+ def add_fd: (id: Integer, source_fd: Integer, ?flags: Integer, ?newfd: Integer, ?newfd_flags: Integer) -> Integer
280
+ def close: () -> nil
281
+ def closed?: () -> bool
282
+ end
283
+
284
+ class Notification
285
+ attr_reader id: Integer
286
+ attr_reader pid: Integer
287
+ attr_reader flags: Integer
288
+ attr_reader syscall: Integer
289
+ attr_reader arch: Integer
290
+ attr_reader instruction_pointer: Integer
291
+ attr_reader args: Array[Integer]
292
+ def initialize: (Notifier, Hash[Symbol, untyped]) -> void
293
+ def valid?: () -> bool
294
+ def respond: (?val: Integer, ?error: errno, ?flags: Integer) -> nil
295
+ def allow: (?Integer) -> nil
296
+ def deny: (errno) -> nil
297
+ def continue!: () -> nil
298
+ def add_fd: (IO, ?flags: Integer, ?newfd: Integer, ?newfd_flags: Integer) -> Integer
299
+ end
300
+ end