tigerbeetle 0.0.34 → 0.0.37
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/ext/tb_client/extconf.rb +13 -13
- data/ext/tb_client/tigerbeetle/LICENSE +177 -0
- data/ext/tb_client/tigerbeetle/build.zig +2327 -0
- data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
- data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
- data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
- data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
- data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
- data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
- data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
- data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
- data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
- data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
- data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
- data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
- data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
- data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
- data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
- data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
- data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
- data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
- data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
- data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
- data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
- data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
- data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
- data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
- data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
- data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
- data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
- data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
- data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
- data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
- data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
- data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
- data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
- data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
- data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
- data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
- data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
- data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
- data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
- data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
- data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
- data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
- data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
- data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
- data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
- data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
- data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
- data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
- data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
- data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
- data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
- data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
- data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
- data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
- data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
- data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
- data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
- data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
- data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
- data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
- data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
- data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
- data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
- data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
- data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
- data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
- data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
- data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
- data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
- data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
- data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
- data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
- data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
- data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
- data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
- data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
- data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
- data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
- data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
- data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
- data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
- data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
- data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
- data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
- data/lib/tb_client/shared_lib.rb +12 -5
- data/lib/tigerbeetle/client.rb +1 -1
- data/lib/tigerbeetle/platforms.rb +9 -0
- data/lib/tigerbeetle/version.rb +2 -2
- data/tigerbeetle.gemspec +22 -5
- metadata +242 -3
- data/ext/tb_client/pkg.tar.gz +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
const stdx = @import("stdx.zig");
|
|
4
|
+
|
|
5
|
+
/// A moment in monotonic time not anchored to any particular epoch.
|
|
6
|
+
///
|
|
7
|
+
/// The absolute value of `ns` is meaningless, but it is possible to compute `Duration` between
|
|
8
|
+
/// two `Instant`s sourced from the same clock.
|
|
9
|
+
///
|
|
10
|
+
/// See also `InstantUnix`.
|
|
11
|
+
pub const Instant = struct {
|
|
12
|
+
ns: u64,
|
|
13
|
+
|
|
14
|
+
pub fn add(now: Instant, duration: Duration) Instant {
|
|
15
|
+
return .{ .ns = now.ns + duration.ns };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pub fn duration_since(now: Instant, earlier: Instant) Duration {
|
|
19
|
+
assert(now.ns >= earlier.ns);
|
|
20
|
+
const elapsed_ns = now.ns - earlier.ns;
|
|
21
|
+
return .{ .ns = elapsed_ns };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/// Non-negative time difference between two `Instant`s.
|
|
26
|
+
pub const Duration = struct {
|
|
27
|
+
ns: u64,
|
|
28
|
+
|
|
29
|
+
pub fn ms(amount_ms: u64) Duration {
|
|
30
|
+
return .{ .ns = amount_ms * std.time.ns_per_ms };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn seconds(amount_seconds: u64) Duration {
|
|
34
|
+
return .{ .ns = amount_seconds * std.time.ns_per_s };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn minutes(amount_minutes: u64) Duration {
|
|
38
|
+
return .{ .ns = amount_minutes * std.time.ns_per_min };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Duration in microseconds, μs, 1/1_000_000 of a second.
|
|
42
|
+
pub fn to_us(duration: Duration) u64 {
|
|
43
|
+
return @divFloor(duration.ns, std.time.ns_per_us);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Duration in milliseconds, ms, 1/1_000 of a second.
|
|
47
|
+
pub fn to_ms(duration: Duration) u64 {
|
|
48
|
+
return @divFloor(duration.ns, std.time.ns_per_ms);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn min(lhs: Duration, rhs: Duration) Duration {
|
|
52
|
+
return .{ .ns = @min(lhs.ns, rhs.ns) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn max(lhs: Duration, rhs: Duration) Duration {
|
|
56
|
+
return .{ .ns = @max(lhs.ns, rhs.ns) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn clamp(duration: Duration, clamp_min: Duration, clamp_max: Duration) Duration {
|
|
60
|
+
assert(clamp_min.ns <= clamp_max.ns);
|
|
61
|
+
if (duration.ns < clamp_min.ns) return clamp_min;
|
|
62
|
+
if (duration.ns > clamp_max.ns) return clamp_max;
|
|
63
|
+
return duration;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub const sort = struct {
|
|
67
|
+
pub fn asc(ctx: void, lhs: Duration, rhs: Duration) bool {
|
|
68
|
+
return std.sort.asc(u64)(ctx, lhs.ns, rhs.ns);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Human readable format like `1.123s`.
|
|
73
|
+
// NB: this is a lossy operation, durations are rounded to look nice.
|
|
74
|
+
pub fn format(
|
|
75
|
+
duration: Duration,
|
|
76
|
+
comptime fmt: []const u8,
|
|
77
|
+
options: std.fmt.FormatOptions,
|
|
78
|
+
writer: anytype,
|
|
79
|
+
) !void {
|
|
80
|
+
try std.fmt.fmtDuration(duration.ns).format(fmt, options, writer);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub fn parse_flag_value(
|
|
84
|
+
string: []const u8,
|
|
85
|
+
static_diagnostic: *?[]const u8,
|
|
86
|
+
) error{InvalidFlagValue}!Duration {
|
|
87
|
+
assert(string.len > 0);
|
|
88
|
+
var string_remaining = string;
|
|
89
|
+
|
|
90
|
+
var result: Duration = .{ .ns = 0 };
|
|
91
|
+
while (string_remaining.len > 0) {
|
|
92
|
+
string_remaining, const component =
|
|
93
|
+
try parse_flag_value_component(string_remaining, static_diagnostic);
|
|
94
|
+
result.ns +|= component.ns;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (result.ns >= 1_000 * std.time.ns_per_day) {
|
|
98
|
+
static_diagnostic.* = "duration too large:";
|
|
99
|
+
return error.InvalidFlagValue;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn parse_flag_value_component(
|
|
105
|
+
string: []const u8,
|
|
106
|
+
static_diagnostic: *?[]const u8,
|
|
107
|
+
) error{InvalidFlagValue}!struct { []const u8, Duration } {
|
|
108
|
+
const split_index = for (string, 0..) |c, index| {
|
|
109
|
+
if (std.ascii.isDigit(c)) {
|
|
110
|
+
// Numeric part continues.
|
|
111
|
+
} else break index;
|
|
112
|
+
} else {
|
|
113
|
+
static_diagnostic.* = "missing unit; must be one of: d/h/m/s/ms/us/ns:";
|
|
114
|
+
return error.InvalidFlagValue;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (split_index == 0) {
|
|
118
|
+
static_diagnostic.* = "missing value:";
|
|
119
|
+
return error.InvalidFlagValue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const string_amount = string[0..split_index];
|
|
123
|
+
const string_remaining = string[split_index..];
|
|
124
|
+
assert(string_amount.len > 0);
|
|
125
|
+
assert(string_remaining.len > 0);
|
|
126
|
+
|
|
127
|
+
const amount = std.fmt.parseInt(u64, string_amount, 10) catch |err| switch (err) {
|
|
128
|
+
error.Overflow => {
|
|
129
|
+
static_diagnostic.* = "integer overflow:";
|
|
130
|
+
return error.InvalidFlagValue;
|
|
131
|
+
},
|
|
132
|
+
error.InvalidCharacter => unreachable,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const Unit = enum(u64) {
|
|
136
|
+
ns = 1,
|
|
137
|
+
us = std.time.ns_per_us,
|
|
138
|
+
ms = std.time.ns_per_ms,
|
|
139
|
+
s = std.time.ns_per_s,
|
|
140
|
+
m = std.time.ns_per_min,
|
|
141
|
+
h = std.time.ns_per_hour,
|
|
142
|
+
d = std.time.ns_per_day,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
inline for (comptime std.enums.values(Unit)) |unit| {
|
|
146
|
+
if (stdx.cut_prefix(string_remaining, @tagName(unit))) |suffix| {
|
|
147
|
+
return .{ suffix, .{ .ns = amount *| @intFromEnum(unit) } };
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
static_diagnostic.* = "unknown unit; must be one of: d/h/m/s/ms/us/ns:";
|
|
151
|
+
return error.InvalidFlagValue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
test "Instant/Duration" {
|
|
157
|
+
const instant_1: Instant = .{ .ns = 100 * std.time.ns_per_day };
|
|
158
|
+
const instant_2: Instant = .{ .ns = 100 * std.time.ns_per_day + std.time.ns_per_s };
|
|
159
|
+
assert(instant_1.duration_since(instant_1).ns == 0);
|
|
160
|
+
assert(instant_2.duration_since(instant_1).ns == std.time.ns_per_s);
|
|
161
|
+
|
|
162
|
+
const duration = instant_2.duration_since(instant_1);
|
|
163
|
+
assert(duration.ns == 1_000_000_000);
|
|
164
|
+
assert(duration.to_us() == 1_000_000);
|
|
165
|
+
assert(duration.to_ms() == 1_000);
|
|
166
|
+
|
|
167
|
+
assert(Duration.ms(1).ns == std.time.ns_per_ms);
|
|
168
|
+
assert(Duration.seconds(1).ns == std.time.ns_per_s);
|
|
169
|
+
assert(Duration.minutes(1).ns == std.time.ns_per_min);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
test "Duration.parse_flag_value" {
|
|
173
|
+
try stdx.parse_flag_value_fuzz(Duration, Duration.parse_flag_value, .{
|
|
174
|
+
.ok = &.{
|
|
175
|
+
.{ "1h", .{ .ns = std.time.ns_per_hour } },
|
|
176
|
+
.{ "1m", .{ .ns = std.time.ns_per_min } },
|
|
177
|
+
.{ "1h2m", .{ .ns = std.time.ns_per_hour + 2 * std.time.ns_per_min } },
|
|
178
|
+
.{ "1ms2us3ns", .{ .ns = std.time.ns_per_ms + 2 * std.time.ns_per_us + 3 } },
|
|
179
|
+
},
|
|
180
|
+
.err = &.{
|
|
181
|
+
.{ "h", "missing value" },
|
|
182
|
+
.{ "1", "missing unit" },
|
|
183
|
+
.{ "h1", "missing value" },
|
|
184
|
+
.{ "1H", "unknown unit; must be one of: d/h/m/s/ms/us/ns" },
|
|
185
|
+
.{ "1h2x", "unknown unit" },
|
|
186
|
+
.{ "1_0h", "unknown unit" },
|
|
187
|
+
.{ "1h 2m", "missing value" },
|
|
188
|
+
.{ "18446744073709551616ns", "integer overflow" },
|
|
189
|
+
.{ "1844674407370955161s", "duration too large" },
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// A moment in non-monotonic Unix time.
|
|
195
|
+
/// Timestamp is relative to epoch 1970-01-1.
|
|
196
|
+
///
|
|
197
|
+
/// See also `Instant`.
|
|
198
|
+
pub const InstantUnix = struct {
|
|
199
|
+
ns: u64,
|
|
200
|
+
|
|
201
|
+
pub fn now() InstantUnix {
|
|
202
|
+
const timestamp_ns = std.time.nanoTimestamp();
|
|
203
|
+
assert(timestamp_ns > 0);
|
|
204
|
+
assert(timestamp_ns <= std.math.maxInt(u64));
|
|
205
|
+
return .{ .ns = @intCast(timestamp_ns) };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pub fn from_timestamp_s(timestamp_s: u64) InstantUnix {
|
|
209
|
+
return InstantUnix{ .ns = timestamp_s * std.time.ms_per_s * std.time.ns_per_ms };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub fn date_time(instant: InstantUnix) struct {
|
|
213
|
+
year: u16,
|
|
214
|
+
month: u8,
|
|
215
|
+
day: u8,
|
|
216
|
+
hour: u8,
|
|
217
|
+
minute: u8,
|
|
218
|
+
second: u8,
|
|
219
|
+
millisecond: u16,
|
|
220
|
+
} {
|
|
221
|
+
const timestamp_ms = @divTrunc(instant.ns, std.time.ns_per_ms);
|
|
222
|
+
const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @divTrunc(timestamp_ms, 1000) };
|
|
223
|
+
const year_day = epoch_seconds.getEpochDay().calculateYearDay();
|
|
224
|
+
const month_day = year_day.calculateMonthDay();
|
|
225
|
+
const time = epoch_seconds.getDaySeconds();
|
|
226
|
+
|
|
227
|
+
return .{
|
|
228
|
+
.year = year_day.year,
|
|
229
|
+
.month = month_day.month.numeric(),
|
|
230
|
+
.day = month_day.day_index + 1,
|
|
231
|
+
.hour = time.getHoursIntoDay(),
|
|
232
|
+
.minute = time.getMinutesIntoHour(),
|
|
233
|
+
.second = time.getSecondsIntoMinute(),
|
|
234
|
+
.millisecond = @intCast(@mod(timestamp_ms, 1000)),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
pub fn format(
|
|
239
|
+
instant: InstantUnix,
|
|
240
|
+
comptime fmt: []const u8,
|
|
241
|
+
options: std.fmt.FormatOptions,
|
|
242
|
+
writer: anytype,
|
|
243
|
+
) !void {
|
|
244
|
+
_ = fmt;
|
|
245
|
+
_ = options;
|
|
246
|
+
const datetime = instant.date_time();
|
|
247
|
+
try writer.print("{d:0>4}-{d:0>2}-{d:0>2} {d:0>2}:{d:0>2}:{d:0>2}.{d:0>3}Z", .{
|
|
248
|
+
datetime.year,
|
|
249
|
+
datetime.month,
|
|
250
|
+
datetime.day,
|
|
251
|
+
datetime.hour,
|
|
252
|
+
datetime.minute,
|
|
253
|
+
datetime.second,
|
|
254
|
+
datetime.millisecond,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
pub fn to_seconds(instant: InstantUnix) u64 {
|
|
259
|
+
return @divFloor(instant.ns, std.time.ns_per_s);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
test "InstantUnix format" {
|
|
264
|
+
const instant_min = InstantUnix{ .ns = 0 };
|
|
265
|
+
var buffer: [24]u8 = undefined;
|
|
266
|
+
try std.testing.expectEqualStrings(
|
|
267
|
+
"1970-01-01 00:00:00.000Z",
|
|
268
|
+
try std.fmt.bufPrint(&buffer, "{}", .{instant_min}),
|
|
269
|
+
);
|
|
270
|
+
const instant_max = InstantUnix{ .ns = std.math.maxInt(u64) };
|
|
271
|
+
try std.testing.expectEqualStrings(
|
|
272
|
+
"2554-07-21 23:34:33.709Z",
|
|
273
|
+
try std.fmt.bufPrint(&buffer, "{}", .{instant_max}),
|
|
274
|
+
);
|
|
275
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
//! Some tools for working with Linux `unshare` and namespaces.
|
|
2
|
+
//!
|
|
3
|
+
//! We use user, pid, and network namespaces for two purposes:
|
|
4
|
+
//!
|
|
5
|
+
//! - Processes namespaces enable all processes in the namespace
|
|
6
|
+
//! to be killed when the namespace's init process is.
|
|
7
|
+
//! - Network namespaces allow us to create an isolated loopback network.
|
|
8
|
+
//!
|
|
9
|
+
//! This code uses the Linux `unshare` syscall to create new
|
|
10
|
+
//! namespaces.
|
|
11
|
+
//!
|
|
12
|
+
//! The main tool here is `maybe_unshare_and_relaunch`, which provides
|
|
13
|
+
//! a pattern for forking a new process that is an init process in
|
|
14
|
+
//! its own process namespace.
|
|
15
|
+
|
|
16
|
+
const std = @import("std");
|
|
17
|
+
const builtin = @import("builtin");
|
|
18
|
+
const linux = std.os.linux;
|
|
19
|
+
const log = std.log.scoped(.unshare);
|
|
20
|
+
const assert = std.debug.assert;
|
|
21
|
+
|
|
22
|
+
// The external pid of the init process of the unshare pid namespace.
|
|
23
|
+
// (Its pid within the namespace is always 1.)
|
|
24
|
+
var child_pid: ?std.process.Child.Id = null;
|
|
25
|
+
|
|
26
|
+
// On receiving SIGTERM, the parent must explicitly kill the pid namespace child process, since
|
|
27
|
+
// it would otherwise keep running. Since the child is the init process, that automatically kills
|
|
28
|
+
// all of its descendants too.
|
|
29
|
+
const trap_action = std.posix.Sigaction{
|
|
30
|
+
.handler = .{ .handler = trap_handler },
|
|
31
|
+
.mask = std.posix.empty_sigset,
|
|
32
|
+
.flags = 0,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
fn trap_handler(signal: i32) callconv(.c) void {
|
|
36
|
+
if (child_pid) |child| {
|
|
37
|
+
std.posix.kill(child, std.posix.SIG.KILL) catch |err| {
|
|
38
|
+
log.err("error killing sandboxed process: {}", .{err});
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
std.posix.exit(@intCast(@as(i32, 128) + signal));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Relaunch this process with new namespaces.
|
|
45
|
+
///
|
|
46
|
+
/// If the current process is already running with the namespaces configured as
|
|
47
|
+
/// requested then this function does nothing. Otherwise it configures the
|
|
48
|
+
/// namespaces and with them spawns a new process with the same arguments as
|
|
49
|
+
/// the current process, waits for it, then exits the process directly (not
|
|
50
|
+
/// returning from this function).
|
|
51
|
+
///
|
|
52
|
+
/// This should generally be called immediately from `main`.
|
|
53
|
+
///
|
|
54
|
+
/// If the `pid` option is provided then the spawned process will be the init
|
|
55
|
+
/// process in a new pid namespace. When it is terminated all subprocesses
|
|
56
|
+
/// transitively will also be terminated.
|
|
57
|
+
///
|
|
58
|
+
/// If the `network` option is provided then the spawned process and its
|
|
59
|
+
/// subprocesses will have loopback network access only.
|
|
60
|
+
pub fn maybe_unshare_and_relaunch(
|
|
61
|
+
gpa: std.mem.Allocator,
|
|
62
|
+
options: struct {
|
|
63
|
+
pid: bool,
|
|
64
|
+
network: bool,
|
|
65
|
+
},
|
|
66
|
+
) !void {
|
|
67
|
+
comptime assert(builtin.os.tag == .linux);
|
|
68
|
+
|
|
69
|
+
if (std.os.linux.getpid() != 1) {
|
|
70
|
+
try linux_unshare(.{
|
|
71
|
+
.pid = options.pid,
|
|
72
|
+
.network = options.network,
|
|
73
|
+
});
|
|
74
|
+
if (options.network) {
|
|
75
|
+
try linux_ip_link_loopback();
|
|
76
|
+
}
|
|
77
|
+
if (options.pid) {
|
|
78
|
+
std.posix.sigaction(std.posix.SIG.TERM, &trap_action, null);
|
|
79
|
+
try fork_and_exit(gpa);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// We are within the pid namespace.
|
|
83
|
+
assert(options.pid);
|
|
84
|
+
assert(std.os.linux.getpid() == 1);
|
|
85
|
+
assert(child_pid == null);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Implementation of `unshare` somewhat like
|
|
90
|
+
///
|
|
91
|
+
/// ```
|
|
92
|
+
/// unshare --user --net --pid
|
|
93
|
+
/// ```
|
|
94
|
+
///
|
|
95
|
+
/// We're trying to accomplish two main things:
|
|
96
|
+
///
|
|
97
|
+
/// - creating a new pid namespace so that all subprocesses
|
|
98
|
+
/// are automatically terminated when pid 1 (the forked
|
|
99
|
+
/// vortex supervisor) is terminated.
|
|
100
|
+
/// - creating a network sandbox
|
|
101
|
+
///
|
|
102
|
+
/// Note that on recent Ubuntu's this only works if AppArmour
|
|
103
|
+
/// rules have been relaxed:
|
|
104
|
+
///
|
|
105
|
+
/// ```
|
|
106
|
+
/// sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
|
|
107
|
+
/// sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
|
108
|
+
/// ```
|
|
109
|
+
pub fn linux_unshare(options: struct {
|
|
110
|
+
pid: bool,
|
|
111
|
+
network: bool,
|
|
112
|
+
}) !void {
|
|
113
|
+
comptime assert(builtin.os.tag == .linux);
|
|
114
|
+
|
|
115
|
+
// Create user namespace first.
|
|
116
|
+
const unshare_user_result = std.os.linux.unshare(linux.CLONE.NEWUSER);
|
|
117
|
+
const unshare_user_errno = std.os.linux.E.init(unshare_user_result);
|
|
118
|
+
if (unshare_user_errno != .SUCCESS) {
|
|
119
|
+
log.err("Failed to create user namespace: {}", .{unshare_user_errno});
|
|
120
|
+
return error.UnshareFailure;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Create PID namespace.
|
|
124
|
+
if (options.pid) {
|
|
125
|
+
const unshare_pid_result = std.os.linux.unshare(linux.CLONE.NEWPID);
|
|
126
|
+
const unshare_pid_errno = std.os.linux.E.init(unshare_pid_result);
|
|
127
|
+
if (unshare_pid_errno != .SUCCESS) {
|
|
128
|
+
log.err("Failed to create pid namespace: {}", .{unshare_pid_errno});
|
|
129
|
+
return error.UnshareFailure;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Create network namespace.
|
|
134
|
+
if (options.network) {
|
|
135
|
+
const unshare_net_result = std.os.linux.unshare(linux.CLONE.NEWNET);
|
|
136
|
+
const unshare_net_errno = std.os.linux.E.init(unshare_net_result);
|
|
137
|
+
if (unshare_net_errno != .SUCCESS) {
|
|
138
|
+
log.err("Failed to create net namespace: {}", .{unshare_net_errno});
|
|
139
|
+
return error.UnshareFailure;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Implementation of `ip link` equivalent to
|
|
145
|
+
///
|
|
146
|
+
/// ```
|
|
147
|
+
/// ip link set up dev lo
|
|
148
|
+
/// ```
|
|
149
|
+
///
|
|
150
|
+
/// This brings up the loopback device so that networking
|
|
151
|
+
/// over 127.0.0.1 works.
|
|
152
|
+
pub fn linux_ip_link_loopback() !void {
|
|
153
|
+
comptime assert(builtin.os.tag == .linux);
|
|
154
|
+
|
|
155
|
+
// Open a netlink socket with the NETLINK.ROUTE protocol.
|
|
156
|
+
const sock = std.posix.socket(
|
|
157
|
+
linux.AF.NETLINK,
|
|
158
|
+
std.posix.SOCK.RAW,
|
|
159
|
+
linux.NETLINK.ROUTE,
|
|
160
|
+
) catch |err| {
|
|
161
|
+
log.err("failed to create netlink socket: {}", .{err});
|
|
162
|
+
return error.IpLink;
|
|
163
|
+
};
|
|
164
|
+
defer std.posix.close(sock);
|
|
165
|
+
|
|
166
|
+
const addr = linux.sockaddr.nl{
|
|
167
|
+
.family = linux.AF.NETLINK,
|
|
168
|
+
.pid = 0,
|
|
169
|
+
.groups = 0,
|
|
170
|
+
};
|
|
171
|
+
std.posix.bind(sock, @ptrCast(&addr), @sizeOf(@TypeOf(addr))) catch |err| {
|
|
172
|
+
log.err("failed to bind netlink socket: {}", .{err});
|
|
173
|
+
return error.IpLink;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Netlink definitions.
|
|
177
|
+
const nlmsghdr = linux.nlmsghdr;
|
|
178
|
+
const ifinfomsg = linux.ifinfomsg;
|
|
179
|
+
|
|
180
|
+
const nlmsgerr = extern struct {
|
|
181
|
+
@"error": c_int,
|
|
182
|
+
msg: nlmsghdr,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const IFF_UP = 0x1;
|
|
186
|
+
|
|
187
|
+
// Our message to the kernel - header plus interface info.
|
|
188
|
+
const Message = extern struct {
|
|
189
|
+
hdr: nlmsghdr,
|
|
190
|
+
ifi: ifinfomsg,
|
|
191
|
+
|
|
192
|
+
comptime {
|
|
193
|
+
assert(@sizeOf(@This()) == @sizeOf(nlmsghdr) + @sizeOf(ifinfomsg));
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Kernel's message to us - header plus error info.
|
|
198
|
+
const Response = extern struct {
|
|
199
|
+
hdr: nlmsghdr,
|
|
200
|
+
err: nlmsgerr,
|
|
201
|
+
|
|
202
|
+
comptime {
|
|
203
|
+
assert(@sizeOf(@This()) == @sizeOf(nlmsghdr) + @sizeOf(nlmsgerr));
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
var msg: Message = .{
|
|
208
|
+
.hdr = .{
|
|
209
|
+
.len = @sizeOf(nlmsghdr) + @sizeOf(ifinfomsg),
|
|
210
|
+
.type = .RTM_NEWLINK,
|
|
211
|
+
// ACK says to always send a response, even on success.
|
|
212
|
+
.flags = linux.NLM_F_REQUEST | linux.NLM_F_ACK,
|
|
213
|
+
.seq = 0,
|
|
214
|
+
.pid = 0,
|
|
215
|
+
},
|
|
216
|
+
.ifi = .{
|
|
217
|
+
.family = linux.AF.UNSPEC,
|
|
218
|
+
.type = 0,
|
|
219
|
+
// Seems to be the loopback device, not sure how
|
|
220
|
+
// to find this value the correct way.
|
|
221
|
+
.index = 1,
|
|
222
|
+
.flags = IFF_UP,
|
|
223
|
+
// man pages say use this value.
|
|
224
|
+
.change = 0xFFFFFFFF,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const msg_buf = std.mem.asBytes(&msg);
|
|
229
|
+
const sent_len = std.posix.sendto(sock, msg_buf, 0, null, 0) catch |err| {
|
|
230
|
+
log.err("failed to send netlink message: {}", .{err});
|
|
231
|
+
return error.IpLink;
|
|
232
|
+
};
|
|
233
|
+
assert(sent_len == msg.hdr.len);
|
|
234
|
+
|
|
235
|
+
var ack: Response = undefined;
|
|
236
|
+
const ack_buf = std.mem.asBytes(&ack);
|
|
237
|
+
const ack_len = std.posix.recv(sock, ack_buf, 0) catch |err| {
|
|
238
|
+
log.err("failed to receive netlink ack: {}", .{err});
|
|
239
|
+
return error.IpLink;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
assert(ack_len == @sizeOf(Response));
|
|
243
|
+
assert(ack.hdr.type == .ERROR);
|
|
244
|
+
assert(ack.err.msg.pid == msg.hdr.pid);
|
|
245
|
+
|
|
246
|
+
if (ack.err.@"error" != 0) {
|
|
247
|
+
log.err("netlink operation failed with errno: {}", .{-ack.err.@"error"});
|
|
248
|
+
return error.IpLink;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
fn fork_and_exit(gpa: std.mem.Allocator) !void {
|
|
253
|
+
const args_ours = std.os.argv;
|
|
254
|
+
|
|
255
|
+
// We get a fresh path to the exe instead of using the original
|
|
256
|
+
// first argument so that the exe path will be correct even if
|
|
257
|
+
// this process's cwd has changed relative to the original exe.
|
|
258
|
+
var exe_path_buffer: [std.fs.max_path_bytes]u8 = undefined;
|
|
259
|
+
const exe_path = try std.fs.selfExePath(&exe_path_buffer);
|
|
260
|
+
|
|
261
|
+
const args_new = try gpa.alloc([]const u8, args_ours.len);
|
|
262
|
+
defer gpa.free(args_new);
|
|
263
|
+
|
|
264
|
+
args_new[0] = exe_path;
|
|
265
|
+
|
|
266
|
+
for (1..args_ours.len) |arg_index| {
|
|
267
|
+
args_new[arg_index] = std.mem.span(args_ours[arg_index]);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
var child = std.process.Child.init(args_new, gpa);
|
|
271
|
+
child.stdin_behavior = .Inherit;
|
|
272
|
+
child.stdout_behavior = .Inherit;
|
|
273
|
+
child.stderr_behavior = .Inherit;
|
|
274
|
+
|
|
275
|
+
try child.spawn();
|
|
276
|
+
|
|
277
|
+
// Set the global pid so that we can kill it if we receive a SIGTERM.
|
|
278
|
+
assert(child_pid == null);
|
|
279
|
+
child_pid = child.id;
|
|
280
|
+
|
|
281
|
+
const result = try child.wait();
|
|
282
|
+
switch (result) {
|
|
283
|
+
.Exited => |code| {
|
|
284
|
+
std.process.exit(code);
|
|
285
|
+
},
|
|
286
|
+
.Signal => |signal| {
|
|
287
|
+
log.info("sandboxed subprocesses exited with signal {}", .{signal});
|
|
288
|
+
std.process.exit(1);
|
|
289
|
+
},
|
|
290
|
+
else => {
|
|
291
|
+
log.err("sandboxed subprocesses exited abnormally", .{});
|
|
292
|
+
std.process.exit(2);
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
}
|