vivarium 0.0.1
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 +7 -0
- data/README.md +114 -0
- data/Rakefile +12 -0
- data/exe/vivariumd +6 -0
- data/lib/vivarium/version.rb +5 -0
- data/lib/vivarium.rb +319 -0
- data/sig/vivarium.rbs +41 -0
- metadata +65 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a7b939d2599fb7e7115ef398bcc83830be78d3b8576a05e5c6d7c1adec4003a1
|
|
4
|
+
data.tar.gz: 39a158bacb6b706a4b77144b47554f027822263662818a9175b1828109538406
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6b17e753a8ccaf72cb969e5fdb8b005d2e31fc2aa10f1d33984d93ba2b7c6a1e2d8e28560b39a4cb9f2860f453a498c3c9c974db9b4b316b4162b78b3e3841e6
|
|
7
|
+
data.tar.gz: 0bcd982884661f9e93ba27c3dc79cb1a952de8c68f715572022917cad708a771e4f4017af4bf12257f7f71ed38119535d14a5745d06666c580d6486c1d15b820
|
data/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Vivarium
|
|
2
|
+
|
|
3
|
+
Vivarium is an observation and sandbox helper for Ruby.
|
|
4
|
+
|
|
5
|
+
It combines:
|
|
6
|
+
|
|
7
|
+
- eBPF LSM monitoring via RbBCC (`vivariumd`)
|
|
8
|
+
- Ruby-side method boundary observation via `TracePoint` (`Vivarium.observe`)
|
|
9
|
+
|
|
10
|
+
The goal is to visualize which Ruby method context triggered low-level events.
|
|
11
|
+
|
|
12
|
+
## Current Scope
|
|
13
|
+
|
|
14
|
+
Implemented in this repository:
|
|
15
|
+
|
|
16
|
+
- BPF LSM hook on `file_open`
|
|
17
|
+
- Shared pinned maps on bpffs
|
|
18
|
+
- `config_targets` (PID -> 0/1)
|
|
19
|
+
- `event_invoked` (array length 64 with `event_t` records)
|
|
20
|
+
- `event_write_pos` (cursor for appending into `event_invoked`)
|
|
21
|
+
- Ruby API `Vivarium.observe do ... end`
|
|
22
|
+
- Registers current PID to `config_targets`
|
|
23
|
+
- On each `:return` / `:c_return`, drains `event_invoked`
|
|
24
|
+
- Prints stack trace + events
|
|
25
|
+
- Clears event slots and cursor
|
|
26
|
+
- Unregisters PID on block exit
|
|
27
|
+
|
|
28
|
+
`event_t` currently:
|
|
29
|
+
|
|
30
|
+
```c
|
|
31
|
+
struct event_t {
|
|
32
|
+
u32 pid;
|
|
33
|
+
char event_name[8]; // "path_open"
|
|
34
|
+
char payload[64]; // opened path (truncated)
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- Linux kernel/environment supporting BPF LSM
|
|
41
|
+
- `libbcc` installed
|
|
42
|
+
- `bpftool` installed (used to resolve `struct file::f_path` offset from BTF)
|
|
43
|
+
- root privileges for `vivariumd`
|
|
44
|
+
- bpffs mounted (typically `/sys/fs/bpf`)
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
Add to Gemfile:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
gem "vivarium"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bundle install
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
1) Start daemon (root):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
sudo bundle exec vivariumd
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
2) Observe in Ruby process:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
require "vivarium"
|
|
72
|
+
|
|
73
|
+
Vivarium.observe do
|
|
74
|
+
File.read("/etc/hosts")
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can override pin directory via `VIVARIUM_BPF_PIN_DIR` on both sides:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
VIVARIUM_BPF_PIN_DIR=/sys/fs/bpf/vivarium bundle exec vivariumd
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
ENV["VIVARIUM_BPF_PIN_DIR"] = "/sys/fs/bpf/vivarium"
|
|
86
|
+
require "vivarium"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
Run tests:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
bundle exec rake test
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Daemon entrypoint:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
bundle exec vivariumd --pin-dir /sys/fs/bpf/vivarium
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Notes
|
|
104
|
+
|
|
105
|
+
- Thread/Ractor-awareness is not yet implemented.
|
|
106
|
+
- `event_invoked` uses fixed 64 slots and wraps around when full.
|
|
107
|
+
- Payload is truncated to 64 bytes in kernel space.
|
|
108
|
+
- Current output format is textual and intended for iteration.
|
|
109
|
+
- `vivariumd` resolves `struct file::f_path` offset from `/sys/kernel/btf/vmlinux` at startup.
|
|
110
|
+
- You can override offset manually with `VIVARIUM_FILE_F_PATH_OFFSET` if auto-detection fails.
|
|
111
|
+
|
|
112
|
+
## Contributing
|
|
113
|
+
|
|
114
|
+
Issues and pull requests are welcome.
|
data/Rakefile
ADDED
data/exe/vivariumd
ADDED
data/lib/vivarium.rb
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fiddle"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "optparse"
|
|
6
|
+
require "pathname"
|
|
7
|
+
require "rbbcc"
|
|
8
|
+
require_relative "vivarium/version"
|
|
9
|
+
|
|
10
|
+
module Vivarium
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
|
|
13
|
+
PIN_DIR = ENV.fetch("VIVARIUM_BPF_PIN_DIR", "/sys/fs/bpf/vivarium")
|
|
14
|
+
CONFIG_TARGETS_PIN = File.join(PIN_DIR, "config_targets")
|
|
15
|
+
EVENT_INVOKED_PIN = File.join(PIN_DIR, "event_invoked")
|
|
16
|
+
EVENT_WRITE_POS_PIN = File.join(PIN_DIR, "event_write_pos")
|
|
17
|
+
|
|
18
|
+
EVENT_NAME_SIZE = 16
|
|
19
|
+
EVENT_PAYLOAD_SIZE = 64
|
|
20
|
+
EVENT_STRUCT_SIZE = 4 + EVENT_NAME_SIZE + EVENT_PAYLOAD_SIZE
|
|
21
|
+
EVENT_CAPACITY = 64
|
|
22
|
+
|
|
23
|
+
Event = Struct.new(:pid, :event_name, :payload, keyword_init: true) do
|
|
24
|
+
def empty?
|
|
25
|
+
pid.to_i.zero? && event_name.to_s.empty? && payload.to_s.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_binary(raw)
|
|
29
|
+
bytes = raw.to_s.b
|
|
30
|
+
bytes = bytes.ljust(EVENT_STRUCT_SIZE, "\x00")
|
|
31
|
+
|
|
32
|
+
pid = bytes[0, 4].unpack1("L<")
|
|
33
|
+
event_name = bytes[4, EVENT_NAME_SIZE].delete("\x00")
|
|
34
|
+
payload = bytes[4 + EVENT_NAME_SIZE, EVENT_PAYLOAD_SIZE].delete("\x00")
|
|
35
|
+
|
|
36
|
+
new(pid: pid, event_name: event_name, payload: payload)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class MapStore
|
|
41
|
+
def initialize(pin_dir: PIN_DIR)
|
|
42
|
+
@pin_dir = pin_dir
|
|
43
|
+
@config_targets = RbBCC::HashTable.from_pin(
|
|
44
|
+
File.join(@pin_dir, "config_targets"),
|
|
45
|
+
"unsigned int",
|
|
46
|
+
"unsigned char",
|
|
47
|
+
keysize: 4,
|
|
48
|
+
leafsize: 1
|
|
49
|
+
)
|
|
50
|
+
@event_invoked = RbBCC::ArrayTable.from_pin(
|
|
51
|
+
File.join(@pin_dir, "event_invoked"),
|
|
52
|
+
"unsigned int",
|
|
53
|
+
"char[#{EVENT_STRUCT_SIZE}]",
|
|
54
|
+
keysize: 4,
|
|
55
|
+
leafsize: EVENT_STRUCT_SIZE
|
|
56
|
+
)
|
|
57
|
+
@event_write_pos = RbBCC::ArrayTable.from_pin(
|
|
58
|
+
File.join(@pin_dir, "event_write_pos"),
|
|
59
|
+
"unsigned int",
|
|
60
|
+
"unsigned int",
|
|
61
|
+
keysize: 4,
|
|
62
|
+
leafsize: 4
|
|
63
|
+
)
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
raise Error, "failed to open pinned maps under #{@pin_dir}: #{e.class}: #{e.message}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def register_pid(pid)
|
|
69
|
+
@config_targets[pid] = 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def unregister_pid(pid)
|
|
73
|
+
@config_targets.delete(pid)
|
|
74
|
+
rescue KeyError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def drain_events
|
|
79
|
+
events = []
|
|
80
|
+
EVENT_CAPACITY.times do |idx|
|
|
81
|
+
ptr = @event_invoked[idx]
|
|
82
|
+
next unless ptr
|
|
83
|
+
|
|
84
|
+
event = Event.from_binary(ptr[0, EVENT_STRUCT_SIZE])
|
|
85
|
+
next if event.empty?
|
|
86
|
+
|
|
87
|
+
events << event
|
|
88
|
+
@event_invoked[idx] = zeroed_event_ptr
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@event_write_pos[0] = 0
|
|
92
|
+
events
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def zeroed_event_ptr
|
|
98
|
+
ptr = Fiddle::Pointer.malloc(EVENT_STRUCT_SIZE)
|
|
99
|
+
ptr[0, EVENT_STRUCT_SIZE] = "\x00" * EVENT_STRUCT_SIZE
|
|
100
|
+
ptr
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Daemon
|
|
105
|
+
BPF_PROGRAM_TEMPLATE = <<~CLANG
|
|
106
|
+
struct path {
|
|
107
|
+
void *mnt;
|
|
108
|
+
void *dentry;
|
|
109
|
+
};
|
|
110
|
+
struct file {
|
|
111
|
+
char __off[__VIVARIUM_F_PATH_OFFSET__];
|
|
112
|
+
struct path f_path;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
struct event_t {
|
|
116
|
+
u32 pid;
|
|
117
|
+
char event_name[16];
|
|
118
|
+
char payload[64];
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
BPF_HASH(config_targets, u32, u32, 1024);
|
|
122
|
+
BPF_ARRAY(event_invoked, struct event_t, 64);
|
|
123
|
+
BPF_ARRAY(event_write_pos, u32, 1);
|
|
124
|
+
|
|
125
|
+
static __always_inline int target_enabled(u32 pid)
|
|
126
|
+
{
|
|
127
|
+
u32 *enabled = config_targets.lookup(&pid);
|
|
128
|
+
if (!enabled) {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
return *enabled == 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
LSM_PROBE(file_open, struct file *file)
|
|
135
|
+
{
|
|
136
|
+
u32 pid = bpf_get_current_pid_tgid() >> 32;
|
|
137
|
+
if (!target_enabled(pid)) {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
u32 zero = 0;
|
|
142
|
+
u32 *write_pos = event_write_pos.lookup(&zero);
|
|
143
|
+
if (!write_pos) {
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
u32 idx = *write_pos & 63;
|
|
148
|
+
__sync_fetch_and_add(write_pos, 1);
|
|
149
|
+
struct event_t ev = {};
|
|
150
|
+
int path_ret;
|
|
151
|
+
ev.pid = pid;
|
|
152
|
+
__builtin_memcpy(ev.event_name, "path_open", 9);
|
|
153
|
+
|
|
154
|
+
path_ret = bpf_d_path(&file->f_path, ev.payload, sizeof(ev.payload));
|
|
155
|
+
if (path_ret < 0) {
|
|
156
|
+
__builtin_memcpy(ev.payload, "<path_error>", 13);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
event_invoked.update(&idx, &ev);
|
|
160
|
+
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
CLANG
|
|
164
|
+
|
|
165
|
+
def initialize(pin_dir: PIN_DIR)
|
|
166
|
+
@pin_dir = pin_dir
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def run
|
|
170
|
+
ensure_root!
|
|
171
|
+
FileUtils.mkdir_p(@pin_dir)
|
|
172
|
+
|
|
173
|
+
f_path_offset = detect_f_path_offset
|
|
174
|
+
program = BPF_PROGRAM_TEMPLATE.gsub("__VIVARIUM_F_PATH_OFFSET__", f_path_offset.to_s)
|
|
175
|
+
|
|
176
|
+
bpf = RbBCC::BCC.new(text: program)
|
|
177
|
+
|
|
178
|
+
config_targets = bpf["config_targets"]
|
|
179
|
+
event_invoked = bpf["event_invoked"]
|
|
180
|
+
event_write_pos = bpf["event_write_pos"]
|
|
181
|
+
|
|
182
|
+
clear_event_slots(event_invoked)
|
|
183
|
+
event_write_pos[0] = 0
|
|
184
|
+
|
|
185
|
+
pin_map(config_targets, File.join(@pin_dir, "config_targets"))
|
|
186
|
+
pin_map(event_invoked, File.join(@pin_dir, "event_invoked"))
|
|
187
|
+
pin_map(event_write_pos, File.join(@pin_dir, "event_write_pos"))
|
|
188
|
+
|
|
189
|
+
puts "[vivariumd] started"
|
|
190
|
+
puts "[vivariumd] pinned maps in #{@pin_dir}"
|
|
191
|
+
puts "[vivariumd] watching LSM file_open (f_path offset=#{f_path_offset})"
|
|
192
|
+
|
|
193
|
+
loop do
|
|
194
|
+
sleep 1
|
|
195
|
+
end
|
|
196
|
+
rescue Interrupt
|
|
197
|
+
puts "\n[vivariumd] stopping"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
def ensure_root!
|
|
203
|
+
return if Process.uid.zero?
|
|
204
|
+
|
|
205
|
+
raise Error, "vivariumd requires root privileges"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def pin_map(table, path)
|
|
209
|
+
File.unlink(path) if File.exist?(path)
|
|
210
|
+
RbBCC::BCC.pin!(table.map_fd, path)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def clear_event_slots(table)
|
|
214
|
+
ptr = Fiddle::Pointer.malloc(EVENT_STRUCT_SIZE)
|
|
215
|
+
ptr[0, EVENT_STRUCT_SIZE] = "\x00" * EVENT_STRUCT_SIZE
|
|
216
|
+
EVENT_CAPACITY.times do |idx|
|
|
217
|
+
table[idx] = ptr
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def detect_f_path_offset
|
|
222
|
+
env_offset = ENV["VIVARIUM_FILE_F_PATH_OFFSET"]
|
|
223
|
+
return Integer(env_offset, 10) if env_offset
|
|
224
|
+
|
|
225
|
+
raw = IO.popen(
|
|
226
|
+
%w[bpftool btf dump file /sys/kernel/btf/vmlinux format raw],
|
|
227
|
+
err: IO::NULL,
|
|
228
|
+
&:read
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
in_file_struct = false
|
|
232
|
+
f_path_bits_offset = nil
|
|
233
|
+
anon_union_bits_offset = nil
|
|
234
|
+
|
|
235
|
+
raw.each_line do |line|
|
|
236
|
+
if line =~ /^\[\d+\] STRUCT 'file' /
|
|
237
|
+
in_file_struct = true
|
|
238
|
+
next
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
if in_file_struct && line.start_with?("[")
|
|
242
|
+
break
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
next unless in_file_struct
|
|
246
|
+
|
|
247
|
+
if (match = line.match(/'f_path'.*bits_offset=(\d+)/))
|
|
248
|
+
f_path_bits_offset = Integer(match[1], 10)
|
|
249
|
+
next
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
if (match = line.match(/'\(anon\)'.*bits_offset=(\d+)/))
|
|
253
|
+
anon_union_bits_offset = Integer(match[1], 10)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if f_path_bits_offset && anon_union_bits_offset && f_path_bits_offset != anon_union_bits_offset
|
|
258
|
+
warn "[vivariumd] BTF offset mismatch: f_path=#{f_path_bits_offset / 8}, (anon)=#{anon_union_bits_offset / 8}; preferring (anon)"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
bits_offset = anon_union_bits_offset || f_path_bits_offset
|
|
262
|
+
if bits_offset
|
|
263
|
+
if (bits_offset % 8).positive?
|
|
264
|
+
raise Error, "unsupported f_path bits offset=#{bits_offset}"
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
if bits_offset >= 1024
|
|
268
|
+
warn "[vivariumd] suspicious f_path offset=#{bits_offset / 8}, fallback to offset=64"
|
|
269
|
+
return 64
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
return bits_offset / 8
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
warn "[vivariumd] could not find struct file::f_path in BTF, fallback to offset=64"
|
|
276
|
+
64
|
|
277
|
+
rescue Errno::ENOENT
|
|
278
|
+
raise Error, "bpftool is required to resolve struct file::f_path offset"
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def self.observe(pin_dir: PIN_DIR, out: $stdout)
|
|
283
|
+
raise ArgumentError, "block is required" unless block_given?
|
|
284
|
+
|
|
285
|
+
store = MapStore.new(pin_dir: pin_dir)
|
|
286
|
+
pid = Process.pid
|
|
287
|
+
store.register_pid(pid)
|
|
288
|
+
|
|
289
|
+
tracer = TracePoint.new(:return, :c_return) do |tp|
|
|
290
|
+
events = store.drain_events
|
|
291
|
+
next if events.empty?
|
|
292
|
+
|
|
293
|
+
out.puts "[vivarium] #{events.size} event(s) at #{tp.defined_class}##{tp.method_id} (#{tp.event})"
|
|
294
|
+
events.each do |event|
|
|
295
|
+
out.puts " pid=#{event.pid} #{event.event_name} payload=#{event.payload.inspect}"
|
|
296
|
+
end
|
|
297
|
+
out.puts " stack:"
|
|
298
|
+
caller_locations(0, 12).each do |loc|
|
|
299
|
+
out.puts " #{loc.path}:#{loc.lineno}:in #{loc.base_label}"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
tracer.enable
|
|
304
|
+
yield
|
|
305
|
+
ensure
|
|
306
|
+
tracer&.disable
|
|
307
|
+
store&.unregister_pid(pid)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def self.run_daemon!(argv = ARGV)
|
|
311
|
+
options = { pin_dir: PIN_DIR }
|
|
312
|
+
OptionParser.new do |opts|
|
|
313
|
+
opts.banner = "Usage: vivariumd [--pin-dir PATH]"
|
|
314
|
+
opts.on("--pin-dir PATH", "Pinned map directory") { |v| options[:pin_dir] = v }
|
|
315
|
+
end.parse!(argv)
|
|
316
|
+
|
|
317
|
+
Daemon.new(pin_dir: options[:pin_dir]).run
|
|
318
|
+
end
|
|
319
|
+
end
|
data/sig/vivarium.rbs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Vivarium
|
|
2
|
+
VERSION: String
|
|
3
|
+
|
|
4
|
+
class Error < ::StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class Event < ::Struct
|
|
8
|
+
attr_accessor pid: Integer?
|
|
9
|
+
attr_accessor event_name: String?
|
|
10
|
+
attr_accessor payload: String?
|
|
11
|
+
|
|
12
|
+
def empty?: bool
|
|
13
|
+
def self.from_binary: (String raw) -> Event
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class MapStore
|
|
17
|
+
def initialize: (?pin_dir: String pin_dir) -> void
|
|
18
|
+
def register_pid: (Integer pid) -> untyped
|
|
19
|
+
def unregister_pid: (Integer pid) -> untyped
|
|
20
|
+
def drain_events: () -> Array[Event]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Daemon
|
|
24
|
+
BPF_PROGRAM: String
|
|
25
|
+
def initialize: (?pin_dir: String pin_dir) -> void
|
|
26
|
+
def run: () -> void
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
PIN_DIR: String
|
|
30
|
+
CONFIG_TARGETS_PIN: String
|
|
31
|
+
EVENT_INVOKED_PIN: String
|
|
32
|
+
EVENT_WRITE_POS_PIN: String
|
|
33
|
+
|
|
34
|
+
EVENT_NAME_SIZE: Integer
|
|
35
|
+
EVENT_PAYLOAD_SIZE: Integer
|
|
36
|
+
EVENT_STRUCT_SIZE: Integer
|
|
37
|
+
EVENT_CAPACITY: Integer
|
|
38
|
+
|
|
39
|
+
def self.observe: (?pin_dir: String pin_dir, ?out: untyped out) { () -> untyped } -> untyped
|
|
40
|
+
def self.run_daemon!: (?Array[String] argv) -> void
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vivarium
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Uchio Kondo
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rbbcc
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.11.2
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.11.2
|
|
26
|
+
description: Vivarium visualizes low-level events such as file open paths and relates
|
|
27
|
+
them to Ruby method boundaries by combining RbBCC (eBPF LSM) and TracePoint.
|
|
28
|
+
email:
|
|
29
|
+
- udzura@udzura.jp
|
|
30
|
+
executables:
|
|
31
|
+
- vivariumd
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- README.md
|
|
36
|
+
- Rakefile
|
|
37
|
+
- exe/vivariumd
|
|
38
|
+
- lib/vivarium.rb
|
|
39
|
+
- lib/vivarium/version.rb
|
|
40
|
+
- sig/vivarium.rbs
|
|
41
|
+
homepage: https://github.com/udzura/vivarium
|
|
42
|
+
licenses: []
|
|
43
|
+
metadata:
|
|
44
|
+
homepage_uri: https://github.com/udzura/vivarium
|
|
45
|
+
source_code_uri: https://github.com/udzura/vivarium
|
|
46
|
+
changelog_uri: https://github.com/udzura/vivarium/blob/main/README.md
|
|
47
|
+
bug_tracker_uri: https://github.com/udzura/vivarium/issues
|
|
48
|
+
rdoc_options: []
|
|
49
|
+
require_paths:
|
|
50
|
+
- lib
|
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: 3.3.0
|
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
requirements: []
|
|
62
|
+
rubygems_version: 4.0.6
|
|
63
|
+
specification_version: 4
|
|
64
|
+
summary: Ruby observation and sandbox helper with RbBCC + TracePoint
|
|
65
|
+
test_files: []
|