fanotify.rb 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: 164eb7087ea0e3327407b27107999b32cb2e47c98021b13f16a4caebd088dc11
4
+ data.tar.gz: 6ddcc04fa61ce86bbce6f613cd5ab05b58769b99a0ff5b686ae316d1ba942be6
5
+ SHA512:
6
+ metadata.gz: 1aab4796ed440d07ff1945ed7ca1e89bfdc7c3a5fe860eeccc1299393551f9f0392eb0529e6a6469f2bdb919444c612fe8176ff24865d5f1d3585183d79bc554
7
+ data.tar.gz: 978b296931fcdf1098993658223129eb6c1c78eacf4f98c410e5664d60ca24f003d829c8f0846d5fadea665db24cd07e91adae07b1bbd1689cf196e42a62714a
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial release.
6
+
7
+ - Native bindings for Linux `fanotify_init(2)` and `fanotify_mark(2)`.
8
+ - Linux fanotify constants, conditional on the build headers.
9
+ - Kernel errors exposed as Ruby `Errno::*` exceptions.
10
+ - Raw file descriptors for use with Ruby `IO`.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matt Christen
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # fanotify.rb
2
+
3
+ A thin, Linux-only Ruby gem for `fanotify_init(2)` and `fanotify_mark(2)`.
4
+ It exposes the syscalls and Linux constants. Use Ruby `IO` for `read(2)`,
5
+ `write(2)`, and `close(2)` on the returned descriptors.
6
+
7
+ ## Build and install
8
+
9
+ Requires Ruby 3.1 or later, a C compiler, make, Ruby development headers,
10
+ and libc/Linux development headers providing `sys/fanotify.h` and both functions.
11
+
12
+ ```sh
13
+ gem build fanotify.rb.gemspec
14
+ gem install ./fanotify.rb-0.1.0.gem
15
+ ```
16
+
17
+ For Bundler, use `gem "fanotify.rb", require: "fanotify"`. The gem name is
18
+ `fanotify.rb`; the Ruby require path is `fanotify` and the module is `Fanotify`.
19
+
20
+ ## API
21
+
22
+ ```ruby
23
+ require "fanotify"
24
+
25
+ fd = Fanotify.init(flags, event_f_flags)
26
+ result = Fanotify.mark(fd, flags, mask, dirfd, pathname)
27
+ ```
28
+
29
+ `init` returns an integer file descriptor. `mark` returns `0` on success.
30
+ Arguments are required and follow the C API in the same order; flags and
31
+ the 64-bit event mask are integers. `pathname` accepts a String, an object
32
+ implementing `to_path`, or `nil` for a C `NULL` pointer. Relative paths are
33
+ resolved by the kernel against `dirfd`; `Fanotify::AT_FDCWD` uses the current
34
+ working directory. Embedded NUL bytes are rejected.
35
+
36
+ Syscall failures raise Ruby's corresponding `Errno::*` exception with the
37
+ kernel's errno. Flags, permissions, filesystem support, and kernel-version
38
+ restrictions are governed by Linux. In particular, many operations require
39
+ `CAP_SYS_ADMIN`; unprivileged groups have additional restrictions.
40
+
41
+ The `Fanotify` module exposes object-like `FAN_*` UAPI constants and
42
+ `FANOTIFY_METADATA_VERSION`, including event masks, initialization/mark flags,
43
+ permission responses, info-record types, and `FAN_EVENT_METADATA_LEN`.
44
+ Constants are conditional on the headers used to compile the extension;
45
+ their presence does not guarantee running-kernel support. Function-like C
46
+ macros are not Ruby methods.
47
+
48
+ There are no watchers, callbacks, recursive traversal, event parsing, or
49
+ automatic descriptor ownership. The caller reads the kernel event records,
50
+ writes responses, and closes the group descriptor and each event descriptor
51
+ (including any reported pidfds).
52
+
53
+ ## Reading events
54
+
55
+ This example uses the default descriptor-based event format. It requires
56
+ permission to create this type of group. Pass an existing directory in `ARGV[0]`.
57
+
58
+ ```ruby
59
+ require "fanotify"
60
+ require "fcntl"
61
+
62
+ fd = Fanotify.init(Fanotify::FAN_CLOEXEC, Fcntl::O_RDONLY)
63
+ group = IO.for_fd(fd, "rb")
64
+
65
+ begin
66
+ Fanotify.mark(fd, Fanotify::FAN_MARK_ADD,
67
+ Fanotify::FAN_OPEN | Fanotify::FAN_EVENT_ON_CHILD,
68
+ Fanotify::AT_FDCWD, ARGV.fetch(0))
69
+
70
+ loop do
71
+ data = group.sysread(4096)
72
+ offset = 0
73
+ while offset < data.bytesize
74
+ event_len, version, _reserved, metadata_len, mask, event_fd, pid =
75
+ data.byteslice(offset, Fanotify::FAN_EVENT_METADATA_LEN).unpack("LCCSQl l")
76
+
77
+ begin
78
+ raise "unsupported metadata version" unless version == Fanotify::FANOTIFY_METADATA_VERSION
79
+ unless metadata_len >= Fanotify::FAN_EVENT_METADATA_LEN &&
80
+ event_len >= metadata_len && event_len <= data.bytesize - offset
81
+ raise "invalid event length"
82
+ end
83
+ puts "mask=#{mask} pid=#{pid} fd=#{event_fd}"
84
+ ensure
85
+ IO.for_fd(event_fd).close if event_fd >= 0
86
+ end
87
+ offset += event_len
88
+ end
89
+ end
90
+ ensure
91
+ group.close
92
+ end
93
+ ```
94
+
95
+ The unpack template uses native byte order and the Linux UAPI field widths.
96
+ Advance by `event_len`, since records may contain additional information
97
+ after the fixed metadata. `FAN_Q_OVERFLOW` reports lost events, and `FAN_NOFD`
98
+ is a sentinel rather than an open descriptor. Groups using `FAN_REPORT_*`
99
+ flags can return other record formats; consult the Linux API documentation.
100
+
101
+ For a permission event, the native `struct fanotify_response` can be written
102
+ with `group.syswrite([event_fd, Fanotify::FAN_ALLOW].pack("lL"))` (open `group`
103
+ in `"r+b"` mode). Respond before closing the event descriptor. `FAN_DENY`,
104
+ audit flags, and extra response records follow the kernel API.
105
+
106
+ See [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html),
107
+ [fanotify_init(2)](https://man7.org/linux/man-pages/man2/fanotify_init.2.html),
108
+ and [fanotify_mark(2)](https://man7.org/linux/man-pages/man2/fanotify_mark.2.html).
109
+
110
+ ## Development
111
+
112
+ ```sh
113
+ bundle install
114
+ bundle exec rake test
115
+ ```
116
+
117
+ The tests compile the extension and exercise constants, argument conversion,
118
+ and kernel errors. The event integration test skips when the kernel denies
119
+ creating a descriptor-based fanotify group; run it with `CAP_SYS_ADMIN` to
120
+ exercise event delivery, relative directory descriptors, removal, and flush.
121
+
122
+ To run the complete suite in a container, including the integration test:
123
+
124
+ ```sh
125
+ sh script/test-container
126
+ # Optional: choose another Ruby image.
127
+ RUBY_IMAGE=ruby:3.1 sh script/test-container
128
+ ```
129
+
130
+ This requires a rootful Docker daemon on Linux (or a Linux VM) whose kernel
131
+ supports fanotify. The script grants `SYS_ADMIN`, mounts the source read-only,
132
+ and builds and tests inside the container. It sets
133
+ `FANOTIFY_REQUIRE_INTEGRATION=1`, which turns an unavailable fanotify group
134
+ into a test failure instead of a skip. You can also set this variable when
135
+ running the suite directly.
136
+
137
+ The GitHub Actions workflow uses the same script on `ubuntu-24.04` VM runners
138
+ with Ruby 3.1 and 3.4 for pushes and pull requests. No repository secrets or
139
+ fully privileged container are needed. A container host with an additional
140
+ seccomp policy must allow `fanotify_init` and `fanotify_mark`; rootless Docker
141
+ and restricted container runners cannot supply the required host capability.
142
+ The container also builds the gem with strict validation, installs it into
143
+ an isolated gem directory, and verifies it loads outside the source checkout.
144
+
145
+ ## Releasing
146
+
147
+ Releases use the manually dispatched `Release` workflow in `.github/workflows/release.yml`.
148
+ It runs the full CI matrix, checks that the tag matches `Fanotify::VERSION`,
149
+ builds and verifies the package, then uses `rubygems/release-gem` to authenticate
150
+ using RubyGems Trusted Publishing and run Bundler's `rake release` task.
151
+ No RubyGems API key secret is needed. The supplied tag must already exist
152
+ and point to the commit selected for the workflow run.
153
+
154
+ Before the first release, create a GitHub environment named `Release` under
155
+ the repository settings. On RubyGems.org, configure a
156
+ [pending trusted publisher](https://rubygems.org/profile/oidc/pending_trusted_publishers)
157
+ with these exact values:
158
+
159
+ | Setting | Value |
160
+ | --- | --- |
161
+ | Gem name | `fanotify.rb` |
162
+ | Repository owner | `mchristen` |
163
+ | Repository name | `fanotify.rb` |
164
+ | Workflow filename | `release.yml` |
165
+ | Environment | `Release` |
166
+
167
+ The workflow is defined directly in this repository; leave the optional
168
+ Workflow Repository Owner and Workflow Repository Name fields blank.
169
+ See the [RubyGems Trusted Publishing guide](https://guides.rubygems.org/trusted-publishing/).
170
+
171
+ For each release:
172
+
173
+ 1. Set the version in `lib/fanotify/version.rb` and update `CHANGELOG.md`.
174
+ 2. Commit the release changes and get passing GitHub Actions checks on that
175
+ commit for every Ruby version. Integration tests must run without skips,
176
+ and the packaged gem must install and load successfully.
177
+ 3. Build the package from a clean checkout of that commit. For version 0.1.0:
178
+
179
+ ```sh
180
+ gem build --strict fanotify.rb.gemspec
181
+ gem unpack ./fanotify.rb-0.1.0.gem --target /tmp/fanotify-release-review
182
+ ```
183
+
184
+ Review the unpacked files. The package should contain only the Ruby source,
185
+ native extension source, README, changelog, and license, with no compiled
186
+ objects or local configuration.
187
+ 4. Tag the tested commit and push the tag:
188
+
189
+ ```sh
190
+ git tag v0.1.0
191
+ git push origin v0.1.0
192
+ ```
193
+
194
+ In GitHub Actions, open **Release**, select **Run workflow**, choose that
195
+ tag under **Use workflow from**, and enter the same tag in the `tag` input.
196
+ The tag must include the release workflow and Bundler release-task setup.
197
+
198
+ 5. Verify `gem install fanotify.rb -v 0.1.0` on Linux and create GitHub release
199
+ notes from the changelog. Published versions cannot be overwritten; fixes
200
+ require a new version.
201
+
202
+ Branch pushes and pull requests only run tests. Publication requires a manual
203
+ workflow run and passing tests on both Ruby versions, including the required
204
+ integration test. A failed test, package check, tag/version mismatch, or tag
205
+ pointing to a different commit prevents publication.
@@ -0,0 +1,262 @@
1
+ /* Linux UAPI constants; availability follows the build headers. */
2
+ #ifdef FAN_ACCESS
3
+ rb_define_const(mod, "FAN_ACCESS", ULL2NUM(FAN_ACCESS));
4
+ #endif
5
+ #ifdef FAN_MODIFY
6
+ rb_define_const(mod, "FAN_MODIFY", ULL2NUM(FAN_MODIFY));
7
+ #endif
8
+ #ifdef FAN_ATTRIB
9
+ rb_define_const(mod, "FAN_ATTRIB", ULL2NUM(FAN_ATTRIB));
10
+ #endif
11
+ #ifdef FAN_CLOSE_WRITE
12
+ rb_define_const(mod, "FAN_CLOSE_WRITE", ULL2NUM(FAN_CLOSE_WRITE));
13
+ #endif
14
+ #ifdef FAN_CLOSE_NOWRITE
15
+ rb_define_const(mod, "FAN_CLOSE_NOWRITE", ULL2NUM(FAN_CLOSE_NOWRITE));
16
+ #endif
17
+ #ifdef FAN_OPEN
18
+ rb_define_const(mod, "FAN_OPEN", ULL2NUM(FAN_OPEN));
19
+ #endif
20
+ #ifdef FAN_MOVED_FROM
21
+ rb_define_const(mod, "FAN_MOVED_FROM", ULL2NUM(FAN_MOVED_FROM));
22
+ #endif
23
+ #ifdef FAN_MOVED_TO
24
+ rb_define_const(mod, "FAN_MOVED_TO", ULL2NUM(FAN_MOVED_TO));
25
+ #endif
26
+ #ifdef FAN_CREATE
27
+ rb_define_const(mod, "FAN_CREATE", ULL2NUM(FAN_CREATE));
28
+ #endif
29
+ #ifdef FAN_DELETE
30
+ rb_define_const(mod, "FAN_DELETE", ULL2NUM(FAN_DELETE));
31
+ #endif
32
+ #ifdef FAN_DELETE_SELF
33
+ rb_define_const(mod, "FAN_DELETE_SELF", ULL2NUM(FAN_DELETE_SELF));
34
+ #endif
35
+ #ifdef FAN_MOVE_SELF
36
+ rb_define_const(mod, "FAN_MOVE_SELF", ULL2NUM(FAN_MOVE_SELF));
37
+ #endif
38
+ #ifdef FAN_OPEN_EXEC
39
+ rb_define_const(mod, "FAN_OPEN_EXEC", ULL2NUM(FAN_OPEN_EXEC));
40
+ #endif
41
+ #ifdef FAN_Q_OVERFLOW
42
+ rb_define_const(mod, "FAN_Q_OVERFLOW", ULL2NUM(FAN_Q_OVERFLOW));
43
+ #endif
44
+ #ifdef FAN_FS_ERROR
45
+ rb_define_const(mod, "FAN_FS_ERROR", ULL2NUM(FAN_FS_ERROR));
46
+ #endif
47
+ #ifdef FAN_OPEN_PERM
48
+ rb_define_const(mod, "FAN_OPEN_PERM", ULL2NUM(FAN_OPEN_PERM));
49
+ #endif
50
+ #ifdef FAN_ACCESS_PERM
51
+ rb_define_const(mod, "FAN_ACCESS_PERM", ULL2NUM(FAN_ACCESS_PERM));
52
+ #endif
53
+ #ifdef FAN_OPEN_EXEC_PERM
54
+ rb_define_const(mod, "FAN_OPEN_EXEC_PERM", ULL2NUM(FAN_OPEN_EXEC_PERM));
55
+ #endif
56
+ #ifdef FAN_PRE_ACCESS
57
+ rb_define_const(mod, "FAN_PRE_ACCESS", ULL2NUM(FAN_PRE_ACCESS));
58
+ #endif
59
+ #ifdef FAN_MNT_ATTACH
60
+ rb_define_const(mod, "FAN_MNT_ATTACH", ULL2NUM(FAN_MNT_ATTACH));
61
+ #endif
62
+ #ifdef FAN_MNT_DETACH
63
+ rb_define_const(mod, "FAN_MNT_DETACH", ULL2NUM(FAN_MNT_DETACH));
64
+ #endif
65
+ #ifdef FAN_EVENT_ON_CHILD
66
+ rb_define_const(mod, "FAN_EVENT_ON_CHILD", ULL2NUM(FAN_EVENT_ON_CHILD));
67
+ #endif
68
+ #ifdef FAN_RENAME
69
+ rb_define_const(mod, "FAN_RENAME", ULL2NUM(FAN_RENAME));
70
+ #endif
71
+ #ifdef FAN_ONDIR
72
+ rb_define_const(mod, "FAN_ONDIR", ULL2NUM(FAN_ONDIR));
73
+ #endif
74
+ #ifdef FAN_CLOSE
75
+ rb_define_const(mod, "FAN_CLOSE", ULL2NUM(FAN_CLOSE));
76
+ #endif
77
+ #ifdef FAN_MOVE
78
+ rb_define_const(mod, "FAN_MOVE", ULL2NUM(FAN_MOVE));
79
+ #endif
80
+ #ifdef FAN_CLOEXEC
81
+ rb_define_const(mod, "FAN_CLOEXEC", ULL2NUM(FAN_CLOEXEC));
82
+ #endif
83
+ #ifdef FAN_NONBLOCK
84
+ rb_define_const(mod, "FAN_NONBLOCK", ULL2NUM(FAN_NONBLOCK));
85
+ #endif
86
+ #ifdef FAN_CLASS_NOTIF
87
+ rb_define_const(mod, "FAN_CLASS_NOTIF", ULL2NUM(FAN_CLASS_NOTIF));
88
+ #endif
89
+ #ifdef FAN_CLASS_CONTENT
90
+ rb_define_const(mod, "FAN_CLASS_CONTENT", ULL2NUM(FAN_CLASS_CONTENT));
91
+ #endif
92
+ #ifdef FAN_CLASS_PRE_CONTENT
93
+ rb_define_const(mod, "FAN_CLASS_PRE_CONTENT", ULL2NUM(FAN_CLASS_PRE_CONTENT));
94
+ #endif
95
+ #ifdef FAN_ALL_CLASS_BITS
96
+ rb_define_const(mod, "FAN_ALL_CLASS_BITS", ULL2NUM(FAN_ALL_CLASS_BITS));
97
+ #endif
98
+ #ifdef FAN_UNLIMITED_QUEUE
99
+ rb_define_const(mod, "FAN_UNLIMITED_QUEUE", ULL2NUM(FAN_UNLIMITED_QUEUE));
100
+ #endif
101
+ #ifdef FAN_UNLIMITED_MARKS
102
+ rb_define_const(mod, "FAN_UNLIMITED_MARKS", ULL2NUM(FAN_UNLIMITED_MARKS));
103
+ #endif
104
+ #ifdef FAN_ENABLE_AUDIT
105
+ rb_define_const(mod, "FAN_ENABLE_AUDIT", ULL2NUM(FAN_ENABLE_AUDIT));
106
+ #endif
107
+ #ifdef FAN_REPORT_PIDFD
108
+ rb_define_const(mod, "FAN_REPORT_PIDFD", ULL2NUM(FAN_REPORT_PIDFD));
109
+ #endif
110
+ #ifdef FAN_REPORT_TID
111
+ rb_define_const(mod, "FAN_REPORT_TID", ULL2NUM(FAN_REPORT_TID));
112
+ #endif
113
+ #ifdef FAN_REPORT_FID
114
+ rb_define_const(mod, "FAN_REPORT_FID", ULL2NUM(FAN_REPORT_FID));
115
+ #endif
116
+ #ifdef FAN_REPORT_DIR_FID
117
+ rb_define_const(mod, "FAN_REPORT_DIR_FID", ULL2NUM(FAN_REPORT_DIR_FID));
118
+ #endif
119
+ #ifdef FAN_REPORT_NAME
120
+ rb_define_const(mod, "FAN_REPORT_NAME", ULL2NUM(FAN_REPORT_NAME));
121
+ #endif
122
+ #ifdef FAN_REPORT_TARGET_FID
123
+ rb_define_const(mod, "FAN_REPORT_TARGET_FID", ULL2NUM(FAN_REPORT_TARGET_FID));
124
+ #endif
125
+ #ifdef FAN_REPORT_FD_ERROR
126
+ rb_define_const(mod, "FAN_REPORT_FD_ERROR", ULL2NUM(FAN_REPORT_FD_ERROR));
127
+ #endif
128
+ #ifdef FAN_REPORT_MNT
129
+ rb_define_const(mod, "FAN_REPORT_MNT", ULL2NUM(FAN_REPORT_MNT));
130
+ #endif
131
+ #ifdef FAN_REPORT_DFID_NAME
132
+ rb_define_const(mod, "FAN_REPORT_DFID_NAME", ULL2NUM(FAN_REPORT_DFID_NAME));
133
+ #endif
134
+ #ifdef FAN_REPORT_DFID_NAME_TARGET
135
+ rb_define_const(mod, "FAN_REPORT_DFID_NAME_TARGET", ULL2NUM(FAN_REPORT_DFID_NAME_TARGET));
136
+ #endif
137
+ #ifdef FAN_ALL_INIT_FLAGS
138
+ rb_define_const(mod, "FAN_ALL_INIT_FLAGS", ULL2NUM(FAN_ALL_INIT_FLAGS));
139
+ #endif
140
+ #ifdef FAN_MARK_ADD
141
+ rb_define_const(mod, "FAN_MARK_ADD", ULL2NUM(FAN_MARK_ADD));
142
+ #endif
143
+ #ifdef FAN_MARK_REMOVE
144
+ rb_define_const(mod, "FAN_MARK_REMOVE", ULL2NUM(FAN_MARK_REMOVE));
145
+ #endif
146
+ #ifdef FAN_MARK_DONT_FOLLOW
147
+ rb_define_const(mod, "FAN_MARK_DONT_FOLLOW", ULL2NUM(FAN_MARK_DONT_FOLLOW));
148
+ #endif
149
+ #ifdef FAN_MARK_ONLYDIR
150
+ rb_define_const(mod, "FAN_MARK_ONLYDIR", ULL2NUM(FAN_MARK_ONLYDIR));
151
+ #endif
152
+ #ifdef FAN_MARK_IGNORED_MASK
153
+ rb_define_const(mod, "FAN_MARK_IGNORED_MASK", ULL2NUM(FAN_MARK_IGNORED_MASK));
154
+ #endif
155
+ #ifdef FAN_MARK_IGNORED_SURV_MODIFY
156
+ rb_define_const(mod, "FAN_MARK_IGNORED_SURV_MODIFY", ULL2NUM(FAN_MARK_IGNORED_SURV_MODIFY));
157
+ #endif
158
+ #ifdef FAN_MARK_FLUSH
159
+ rb_define_const(mod, "FAN_MARK_FLUSH", ULL2NUM(FAN_MARK_FLUSH));
160
+ #endif
161
+ #ifdef FAN_MARK_EVICTABLE
162
+ rb_define_const(mod, "FAN_MARK_EVICTABLE", ULL2NUM(FAN_MARK_EVICTABLE));
163
+ #endif
164
+ #ifdef FAN_MARK_IGNORE
165
+ rb_define_const(mod, "FAN_MARK_IGNORE", ULL2NUM(FAN_MARK_IGNORE));
166
+ #endif
167
+ #ifdef FAN_MARK_INODE
168
+ rb_define_const(mod, "FAN_MARK_INODE", ULL2NUM(FAN_MARK_INODE));
169
+ #endif
170
+ #ifdef FAN_MARK_MOUNT
171
+ rb_define_const(mod, "FAN_MARK_MOUNT", ULL2NUM(FAN_MARK_MOUNT));
172
+ #endif
173
+ #ifdef FAN_MARK_FILESYSTEM
174
+ rb_define_const(mod, "FAN_MARK_FILESYSTEM", ULL2NUM(FAN_MARK_FILESYSTEM));
175
+ #endif
176
+ #ifdef FAN_MARK_MNTNS
177
+ rb_define_const(mod, "FAN_MARK_MNTNS", ULL2NUM(FAN_MARK_MNTNS));
178
+ #endif
179
+ #ifdef FAN_MARK_IGNORE_SURV
180
+ rb_define_const(mod, "FAN_MARK_IGNORE_SURV", ULL2NUM(FAN_MARK_IGNORE_SURV));
181
+ #endif
182
+ #ifdef FAN_ALL_MARK_FLAGS
183
+ rb_define_const(mod, "FAN_ALL_MARK_FLAGS", ULL2NUM(FAN_ALL_MARK_FLAGS));
184
+ #endif
185
+ #ifdef FAN_ALL_EVENTS
186
+ rb_define_const(mod, "FAN_ALL_EVENTS", ULL2NUM(FAN_ALL_EVENTS));
187
+ #endif
188
+ #ifdef FAN_ALL_PERM_EVENTS
189
+ rb_define_const(mod, "FAN_ALL_PERM_EVENTS", ULL2NUM(FAN_ALL_PERM_EVENTS));
190
+ #endif
191
+ #ifdef FAN_ALL_OUTGOING_EVENTS
192
+ rb_define_const(mod, "FAN_ALL_OUTGOING_EVENTS", ULL2NUM(FAN_ALL_OUTGOING_EVENTS));
193
+ #endif
194
+ #ifdef FANOTIFY_METADATA_VERSION
195
+ rb_define_const(mod, "FANOTIFY_METADATA_VERSION", ULL2NUM(FANOTIFY_METADATA_VERSION));
196
+ #endif
197
+ #ifdef FAN_EVENT_INFO_TYPE_FID
198
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_FID", ULL2NUM(FAN_EVENT_INFO_TYPE_FID));
199
+ #endif
200
+ #ifdef FAN_EVENT_INFO_TYPE_DFID_NAME
201
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_DFID_NAME", ULL2NUM(FAN_EVENT_INFO_TYPE_DFID_NAME));
202
+ #endif
203
+ #ifdef FAN_EVENT_INFO_TYPE_DFID
204
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_DFID", ULL2NUM(FAN_EVENT_INFO_TYPE_DFID));
205
+ #endif
206
+ #ifdef FAN_EVENT_INFO_TYPE_PIDFD
207
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_PIDFD", ULL2NUM(FAN_EVENT_INFO_TYPE_PIDFD));
208
+ #endif
209
+ #ifdef FAN_EVENT_INFO_TYPE_ERROR
210
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_ERROR", ULL2NUM(FAN_EVENT_INFO_TYPE_ERROR));
211
+ #endif
212
+ #ifdef FAN_EVENT_INFO_TYPE_RANGE
213
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_RANGE", ULL2NUM(FAN_EVENT_INFO_TYPE_RANGE));
214
+ #endif
215
+ #ifdef FAN_EVENT_INFO_TYPE_MNT
216
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_MNT", ULL2NUM(FAN_EVENT_INFO_TYPE_MNT));
217
+ #endif
218
+ #ifdef FAN_EVENT_INFO_TYPE_OLD_DFID_NAME
219
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_OLD_DFID_NAME", ULL2NUM(FAN_EVENT_INFO_TYPE_OLD_DFID_NAME));
220
+ #endif
221
+ #ifdef FAN_EVENT_INFO_TYPE_NEW_DFID_NAME
222
+ rb_define_const(mod, "FAN_EVENT_INFO_TYPE_NEW_DFID_NAME", ULL2NUM(FAN_EVENT_INFO_TYPE_NEW_DFID_NAME));
223
+ #endif
224
+ #ifdef FAN_RESPONSE_INFO_NONE
225
+ rb_define_const(mod, "FAN_RESPONSE_INFO_NONE", ULL2NUM(FAN_RESPONSE_INFO_NONE));
226
+ #endif
227
+ #ifdef FAN_RESPONSE_INFO_AUDIT_RULE
228
+ rb_define_const(mod, "FAN_RESPONSE_INFO_AUDIT_RULE", ULL2NUM(FAN_RESPONSE_INFO_AUDIT_RULE));
229
+ #endif
230
+ #ifdef FAN_ALLOW
231
+ rb_define_const(mod, "FAN_ALLOW", ULL2NUM(FAN_ALLOW));
232
+ #endif
233
+ #ifdef FAN_DENY
234
+ rb_define_const(mod, "FAN_DENY", ULL2NUM(FAN_DENY));
235
+ #endif
236
+ #ifdef FAN_ERRNO_BITS
237
+ rb_define_const(mod, "FAN_ERRNO_BITS", ULL2NUM(FAN_ERRNO_BITS));
238
+ #endif
239
+ #ifdef FAN_ERRNO_SHIFT
240
+ rb_define_const(mod, "FAN_ERRNO_SHIFT", ULL2NUM(FAN_ERRNO_SHIFT));
241
+ #endif
242
+ #ifdef FAN_ERRNO_MASK
243
+ rb_define_const(mod, "FAN_ERRNO_MASK", ULL2NUM(FAN_ERRNO_MASK));
244
+ #endif
245
+ #ifdef FAN_AUDIT
246
+ rb_define_const(mod, "FAN_AUDIT", ULL2NUM(FAN_AUDIT));
247
+ #endif
248
+ #ifdef FAN_INFO
249
+ rb_define_const(mod, "FAN_INFO", ULL2NUM(FAN_INFO));
250
+ #endif
251
+ #ifdef FAN_NOFD
252
+ rb_define_const(mod, "FAN_NOFD", INT2NUM(FAN_NOFD));
253
+ #endif
254
+ #ifdef FAN_NOPIDFD
255
+ rb_define_const(mod, "FAN_NOPIDFD", INT2NUM(FAN_NOPIDFD));
256
+ #endif
257
+ #ifdef FAN_EPIDFD
258
+ rb_define_const(mod, "FAN_EPIDFD", INT2NUM(FAN_EPIDFD));
259
+ #endif
260
+ #ifdef FAN_EVENT_METADATA_LEN
261
+ rb_define_const(mod, "FAN_EVENT_METADATA_LEN", ULL2NUM(FAN_EVENT_METADATA_LEN));
262
+ #endif
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ abort "fanotify requires Linux" unless RUBY_PLATFORM.include?("linux")
6
+ abort "sys/fanotify.h is required" unless have_header("sys/fanotify.h")
7
+ abort "fanotify_init(2) is unavailable" unless have_func("fanotify_init", "sys/fanotify.h")
8
+ abort "fanotify_mark(2) is unavailable" unless have_func("fanotify_mark", "sys/fanotify.h")
9
+
10
+ create_makefile("fanotify/fanotify")
@@ -0,0 +1,60 @@
1
+ #ifndef _GNU_SOURCE
2
+ #define _GNU_SOURCE 1
3
+ #endif
4
+
5
+ #include <ruby.h>
6
+ #include <fcntl.h>
7
+ #include <stdint.h>
8
+ #include <sys/fanotify.h>
9
+
10
+ /* Fanotify.init(flags, event_f_flags) -> Integer file descriptor */
11
+ static VALUE
12
+ fanotify_rb_init(VALUE self, VALUE flags, VALUE event_f_flags)
13
+ {
14
+ unsigned int c_flags = NUM2UINT(flags);
15
+ unsigned int c_event_f_flags = NUM2UINT(event_f_flags);
16
+ int fd = fanotify_init(c_flags, c_event_f_flags);
17
+
18
+ if (fd == -1)
19
+ rb_sys_fail("fanotify_init");
20
+
21
+ return INT2NUM(fd);
22
+ }
23
+
24
+ /* Fanotify.mark(fanotify_fd, flags, mask, dirfd, pathname) -> 0 */
25
+ static VALUE
26
+ fanotify_rb_mark(VALUE self, VALUE fanotify_fd, VALUE flags, VALUE mask,
27
+ VALUE dirfd, VALUE pathname)
28
+ {
29
+ int c_fd = NUM2INT(fanotify_fd);
30
+ unsigned int c_flags = NUM2UINT(flags);
31
+ uint64_t c_mask = NUM2ULL(mask);
32
+ int c_dirfd = NUM2INT(dirfd);
33
+ const char *c_path = NULL;
34
+ int result;
35
+
36
+ if (!NIL_P(pathname)) {
37
+ pathname = rb_get_path(pathname);
38
+ c_path = StringValueCStr(pathname);
39
+ }
40
+
41
+ result = fanotify_mark(c_fd, c_flags, c_mask, c_dirfd, c_path);
42
+ RB_GC_GUARD(pathname);
43
+
44
+ if (result == -1)
45
+ rb_sys_fail("fanotify_mark");
46
+
47
+ return INT2NUM(result);
48
+ }
49
+
50
+ RUBY_FUNC_EXPORTED void
51
+ Init_fanotify(void)
52
+ {
53
+ VALUE mod = rb_define_module("Fanotify");
54
+
55
+ rb_define_singleton_method(mod, "init", fanotify_rb_init, 2);
56
+ rb_define_singleton_method(mod, "mark", fanotify_rb_mark, 5);
57
+
58
+ rb_define_const(mod, "AT_FDCWD", INT2NUM(AT_FDCWD));
59
+ #include "constants.h"
60
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fanotify
4
+ VERSION = "0.1.0"
5
+ end
data/lib/fanotify.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "fanotify/version"
4
+ require "fanotify/fanotify"
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fanotify.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Christen
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Native bindings for fanotify_init(2) and fanotify_mark(2), with Linux
13
+ fanotify constants.
14
+ email:
15
+ - oss@christen.tech
16
+ executables: []
17
+ extensions:
18
+ - ext/fanotify/extconf.rb
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - ext/fanotify/constants.h
25
+ - ext/fanotify/extconf.rb
26
+ - ext/fanotify/fanotify.c
27
+ - lib/fanotify.rb
28
+ - lib/fanotify/version.rb
29
+ homepage: https://github.com/mchristen/fanotify.rb
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ source_code_uri: https://github.com/mchristen/fanotify.rb
34
+ bug_tracker_uri: https://github.com/mchristen/fanotify.rb/issues
35
+ changelog_uri: https://github.com/mchristen/fanotify.rb/blob/main/CHANGELOG.md
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '3.1'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.6.9
51
+ specification_version: 4
52
+ summary: Thin Ruby bindings for Linux fanotify syscalls
53
+ test_files: []