diff --git a/doc/acceptance-case-matrix.md b/doc/acceptance-case-matrix.md index 88f6102aa..cf7858260 100644 --- a/doc/acceptance-case-matrix.md +++ b/doc/acceptance-case-matrix.md @@ -478,7 +478,7 @@ this matrix. The README's own code is no doctest. | `nvx-rs/src/activation/mod.rs - activation (line 130)` | kept | | | `nvx-rs/src/exec/mod.rs - exec (line 113)` | retired | The exec engine leaves (SL6.11a) | | `nvx-rs/src/gpt/mod.rs - gpt (line 43)` | kept | | -| `nvx-rs/src/guest/activation.rs - guest::activation (line 151)` | kept | SL6.11c rewrote it on `guest::snapshot::capture`, when its capture preparation and admission fence left (#1087) | +| `nvx-rs/src/guest/activation.rs - guest::activation (line 127)` | kept | SL6.11c rewrote it on `guest::snapshot::capture`, when its capture preparation and admission fence left (#1087) | | `nvx-rs/src/guest/activation/state.rs - guest::activation::state::Activation (line 54)` | kept | SL6.13b (#1127) rewrote it without the guest's space, which became private: its agent applies the record in its own space | | `nvx-rs/src/guest/cgroup.rs - guest::cgroup (line 36)` | retired | `guest::cgroup` leaves (SL6.11b) | | `nvx-rs/src/guest/entry.rs - guest::entry (line 90)` | retired | `guest::entry` leaves (SL6.11b) | @@ -486,7 +486,7 @@ this matrix. The README's own code is no doctest. | `nvx-rs/src/guest/exec.rs - guest::exec::Execution::adopt (line 619)` | retired | `guest::exec` leaves (SL6.11b) | | `nvx-rs/src/guest/gpt.rs - guest::gpt (line 18)` | kept | SL6.13b (#1127) moved it to the module's unit tests, `nvx-rs/src/guest/gpt/tests.rs::a_disk_s_steps_compile`, since the guest's space became private | | `nvx-rs/src/guest/mappings/mapper.rs - guest::mappings::mapper (line 107)` | kept | SL6.11d (#1123) moved it to the mapper's unit tests, `nvx-rs/src/guest/mappings/mapper/tests.rs::a_mapper_s_steps_compile`, which map and unmount through the seams that NVX's programs use, since the mapper's `apply` and `teardown`, which only tests used, left | -| `nvx-rs/src/guest/mappings/table.rs - guest::mappings::table::MappingTable (line 47)` | kept | | +| `nvx-rs/src/guest/mappings/table.rs - guest::mappings::table::MappingTable (line 46)` | kept | | | `nvx-rs/src/guest/mod.rs - guest (line 33)` | kept | The guest-agent guidelines quote it | | `nvx-rs/src/guest/nvxs.rs - guest::nvxs (line 22)` | kept | | | `nvx-rs/src/guest/process.rs - guest::process (line 12)` | retired | `guest::process` leaves (SL6.11b) | @@ -497,7 +497,7 @@ this matrix. The README's own code is no doctest. | `nvx-rs/src/guest/spawn.rs - guest::spawn (line 41)` | retired | `guest::spawn` leaves (SL6.11b) | | `nvx-rs/src/guest/spawn.rs - guest::spawn::LaunchStage::from_code (line 201)` | retired | `LaunchStage::from_code` and its test, `launch_stages_keep_their_numbers`, leave with `guest::spawn` (SL6.11b). The stages of nvx-init's executions keep their numbers in the crate's own fork boundary, whose launch report `nvx-rs/src/guest/init/tests/exec.rs::native_workload_cwd_accepts_procfs_links_but_closes_inherited_descriptors` reads from a real launch | | `nvx-rs/src/guest/terminal.rs - guest::terminal (line 10)` | kept | | -| `nvx-rs/src/guest/time/mod.rs - guest::time (line 9)` | kept | SL6.11c removed its capture and restore finish, which became crate-private: `guest::snapshot::capture` runs them | +| `nvx-rs/src/guest/time/mod.rs - guest::time (line 10)` | kept | SL6.11c removed its capture and restore finish, which became crate-private: `guest::snapshot::capture` runs them | | `nvx-rs/src/host/sandbox/prepared.rs - host::sandbox::prepared::PreparedLaunch (line 92)` | retired | `PreparedLaunch` leaves with the checked-file handoff (SL6.3, #1122) | | `nvx-rs/src/host/store/mod.rs - host::store (line 72)` | retired | The sandbox store leaves (SL6.1, #1121) | | `nvx-rs/src/host/supervisor/mod.rs - host::supervisor (line 32)` | retired | The store-coupled supervisor leaves with the sandbox store (SL6.1, #1121), and `host::supervisor`, the transitional public path of the low-level spawn, with SL6.3 (#1122): `host::vm` spawns VMMs, and the tests of the spawn moved to `nvx-rs/src/host/spawn/tests.rs` and `nvx-rs/src/host/process/tests/orchestrator.rs` | @@ -505,9 +505,9 @@ this matrix. The README's own code is no doctest. | `nvx-rs/src/host/template/plan.rs - host::template::plan::TemplatePlan (line 137)` | retired | `TemplatePlan` leaves with the template store (SL6.2a, #1121) | | `nvx-rs/src/host/template/store.rs - host::template::store::Store (line 473)` | retired | The template store leaves (SL6.2a, #1121) | | `nvx-rs/src/host/transport.rs - host::transport (line 22)` | kept | | -| `nvx-rs/src/lib.rs - (line 58)` | rewritten | Its `Instance` and sandbox store leave (SL6.1, #1121): `nvx-rs/src/lib.rs - (line 53)` compiles a cold boot and a stop through `host::vm` | +| `nvx-rs/src/lib.rs - (line 58)` | rewritten | Its `Instance` and sandbox store leave (SL6.1, #1121): `nvx-rs/src/lib.rs - (line 67)` compiles a cold boot and a stop through `host::vm` | | `nvx-rs/src/protocol/nvxs/mod.rs - protocol::nvxs (line 68)` | kept | | | `nvx-rs/src/host/snapshot/capture.rs - host::snapshot::capture::finish_capture (line 48)` | kept | | | `nvx-rs/src/host/store/mod.rs - host::store (line 104)` | retired | The sandbox store leaves (SL6.1, #1121) | -| `nvx-rs/README.md`, its Example: an instance through `LaunchSpec` and the sandbox store | rewritten | Nothing compiles it. `nvx-rs/examples/host-workflows.rs` compiles the documented host workflows, and the library's guide (SL6.10a) replaces the example. SL6.1 (#1121) made it the cold boot that `nvx-rs/src/lib.rs - (line 53)` compiles | +| `nvx-rs/README.md`, its Example: an instance through `LaunchSpec` and the sandbox store | rewritten | Nothing compiles it. `nvx-rs/examples/host-workflows.rs` compiles the documented host workflows, and the library's guide (SL6.10a) replaces the example. SL6.1 (#1121) made it the cold boot that `nvx-rs/src/lib.rs - (line 67)` compiles | | `nvx-rs/examples/external-launch.rs` | kept | Compiled with the crate's targets, and run by hand on a host. Since SL6.3 (#1122), which deletes `host::supervisor`, a minimal launcher of its own on `std::process` starts its VMM | diff --git a/doc/ci.md b/doc/ci.md index 31e24b5af..2f69da06e 100644 --- a/doc/ci.md +++ b/doc/ci.md @@ -2432,21 +2432,24 @@ library's `gpt::crc::` tests, in two jobs. ## Module diagrams -[`doc/modules`](modules/README.md) publishes diagrams of the modules of the -[`nvx` crate](../nvx-rs/README.md): its top-level modules, the dependencies +[`doc/modules`](modules/README.md) publishes diagrams of the public modules of +the [`nvx` crate](../nvx-rs/README.md): its top-level modules, the dependencies between them, and the modules that each contains, on Linux and Windows. [`module_diagrams.py`](../scripts/module_diagrams.py) draws them. It exports the modules of the crate's library, with its `cli`, `testing`, and `guest` features, for each platform's target with -[cargo-modules](https://github.com/regexident/cargo-modules), merges the -platforms' modules, lays out the diagrams with Graphviz, and writes them with -the page that shows them. Unless it analyzes with `cfg(test)`, cargo-modules -drops as a test module every module whose cfg holds when `test` holds and every -other condition fails, such as one under `cfg(not(windows))`, so the script -also reads its analysis with `cfg(test)` and restores those modules, with their -own uses in place of those that the plain analysis moves to the modules that -contain them, and stops at a cfg that depends on `test` in another way. Nobody -edits the diagrams by hand: +[cargo-modules](https://github.com/regexident/cargo-modules), keeps those of +each platform's public API, without the modules of NVX's own programs, which +the crate hides and the script lists, merges the platforms' modules, lays out +the diagrams with Graphviz, and writes them with the page that shows them. +Unless it analyzes with `cfg(test)`, cargo-modules drops as a test module every +module whose cfg holds when `test` holds and every other condition fails, such +as one under `cfg(not(windows))`, so the script also reads its analysis with +`cfg(test)` and restores those modules, with their own uses in place of those +that the plain analysis moves to the modules that contain them, and stops at a +cfg that depends on `test` in another way, but for `any(test, feature = "cli")` +and its like, whose feature the script enables. Nobody edits the diagrams by +hand: [`module-diagrams.yml`](../.github/workflows/module-diagrams.yml) draws them again every night, with the nightly full run of CI, and commits them to `dev` when they changed, so that `dev` takes at most one push of them a night outside diff --git a/doc/modules/README.md b/doc/modules/README.md index f29641690..a818d636e 100644 --- a/doc/modules/README.md +++ b/doc/modules/README.md @@ -2,22 +2,23 @@ # Module diagrams -The modules of the [`nvx` crate](../../nvx-rs/README.md), the modules that each -contains, and the dependencies between them, as +The public modules of the [`nvx` crate](../../nvx-rs/README.md), the modules +that each contains, and the dependencies between them, as [cargo-modules](https://github.com/regexident/cargo-modules) 0.27.0 finds them -and Graphviz 2.43.0 draws them. They cover the crate's library with its `cli`, -`testing`, and `guest` features, on Linux (`x86_64-unknown-linux-musl`) and -Windows (`x86_64-pc-windows-msvc`). CI draws them again after every merge to -`dev` that changes the crate, and commits them when they change; see [Module +and Graphviz 2.43.0 draws them. They cover the crate's public API, as its +library builds with its `cli`, `testing`, and `guest` features, on Linux +(`x86_64-unknown-linux-musl`) and Windows (`x86_64-pc-windows-msvc`): each +module that is public, as are the modules that contain it, but the modules of +NVX's own programs, which the crate hides. CI draws them again every night, and +commits them to `dev` when they change; see [Module diagrams](../ci.md#module-diagrams). -Each box is a module, colored by the part of the crate that holds it: blue for -the modules that the host and the guest share, purple for the host, teal for -the guest, and amber for the test doubles. A solid box is a public module, and -a dashed box a private one, unless it names another visibility, such as -`pub(crate)`. A box that names platforms, as in *Linux only*, is a module that -only they build, as are the modules that it contains. The diagrams leave out -the crate's tests and binaries, and other crates. +Each box is a public module, colored by the part of the crate that holds it: +blue for the modules that the host and the guest share, purple for the host, +and teal for the guest. A box that names platforms, as in *Linux only*, is a +module that only they build, as are the modules that it contains. The diagrams +leave out the crate's private modules, its tests and binaries, and other +crates. ## Overview @@ -34,7 +35,8 @@ uses the second, as cargo-modules finds them: a module uses another whose items it imports, re-exports included, or whose items the definitions of its own items name, which counts the implementations of a type's traits toward the type's module, wherever they are. The crate's own arrows are its re-exports. -The uses within a top-level module are left out. +The uses within a top-level module, and those of the modules that the diagrams +leave out, are left out. ![Dependencies](dependencies.svg) @@ -43,32 +45,17 @@ The uses within a top-level module are left out. The modules that each top-level module contains. An arrow points from a module to a module that it contains. -### `nvx::activation` - -`nvx::activation`, in -[`nvx-rs/src/activation/mod.rs`](../../nvx-rs/src/activation/mod.rs), contains -2 modules. - -![The modules that nvx::activation contains](nvx-activation.svg) - -### `nvx::gpt` - -`nvx::gpt`, in [`nvx-rs/src/gpt/mod.rs`](../../nvx-rs/src/gpt/mod.rs), contains -5 modules. - -![The modules that nvx::gpt contains](nvx-gpt.svg) - ### `nvx::guest` `nvx::guest`, in [`nvx-rs/src/guest/mod.rs`](../../nvx-rs/src/guest/mod.rs), -contains 97 modules. +contains 7 modules. ![The modules that nvx::guest contains](nvx-guest.svg) ### `nvx::host` `nvx::host`, in [`nvx-rs/src/host/mod.rs`](../../nvx-rs/src/host/mod.rs), -contains 135 modules. +contains 11 modules. ![The modules that nvx::host contains](nvx-host.svg) @@ -82,15 +69,7 @@ contains 135 modules. ### `nvx::protocol` `nvx::protocol`, in -[`nvx-rs/src/protocol/mod.rs`](../../nvx-rs/src/protocol/mod.rs), contains 15 +[`nvx-rs/src/protocol/mod.rs`](../../nvx-rs/src/protocol/mod.rs), contains 9 modules. ![The modules that nvx::protocol contains](nvx-protocol.svg) - -### `nvx::testing` - -`nvx::testing`, in -[`nvx-rs/src/testing/mod.rs`](../../nvx-rs/src/testing/mod.rs), contains 1 -module. - -![The modules that nvx::testing contains](nvx-testing.svg) diff --git a/doc/modules/dependencies.svg b/doc/modules/dependencies.svg index 65d0429a0..3c917bce2 100644 --- a/doc/modules/dependencies.svg +++ b/doc/modules/dependencies.svg @@ -4,248 +4,118 @@ - - + + nvx dependencies - - - -nvx->nvx::error - - - - - -1 - - - -nvx->nvx::host - - - - - -2 - - - -nvx->nvx::hypervisor - - - - - -1 - - - -nvx::activation->nvx::host - - - - - -1 - + - + nvx::activation->nvx::protocol - - - + + + -2 +1 - + nvx::activation->nvx::uuid - - - - - -2 - - - -nvx::error->nvx::host - - - + + + -2 +1 - + nvx::gpt->nvx::uuid - - - + + + -2 - - - -nvx::guest->nvx::activation - - - - - -2 - - - -nvx::guest->nvx::exec - - - - - -2 - - - -nvx::guest->nvx::gpt - - - - - -7 +1 - + nvx::guest->nvx::protocol - - - + + + -22 - - - -nvx::guest->nvx::uuid - - - - - -2 +3 - + nvx::host->nvx::activation - - - + + + -12 +4 - + nvx::host->nvx::contract - - - - - -6 - - - -nvx::host->nvx::error - - - + + + -88 - - - -nvx::host->nvx::hypervisor - - - - - -6 +2 - + nvx::host->nvx::net - - - + + + -5 +2 - + nvx::host->nvx::protocol - - - + + + -35 - - - -nvx::host->nvx::uuid - - - - +7 -4 - - - -nvx::hypervisor->nvx::error - - - + + +nvx::protocol->nvx::activation + + + -1 +2 - + nvx::protocol->nvx::uuid - - - - - -1 - - - -nvx::testing->nvx::protocol - - - + + + -6 +1 nvx - -nvx -crate re-exports + +nvx +crate re-exports @@ -253,8 +123,8 @@ nvx::activation - -activation + +activation @@ -262,101 +132,63 @@ nvx::contract - -contract - - - - - -nvx::error - - -error -pub(crate) - - - - - -nvx::exec - - -exec + +contract - + nvx::gpt - - -gpt + + +gpt - + nvx::guest - - -guest -Linux only + + +guest +Linux only - + nvx::host - - -host - - - - - -nvx::hypervisor - - -hypervisor -pub(crate) + + +host - + nvx::net - - -net + + +net - + nvx::protocol - - -protocol - - - - - -nvx::testing - - -testing + + +protocol - + nvx::uuid - - -uuid + + +uuid diff --git a/doc/modules/nvx-activation.svg b/doc/modules/nvx-activation.svg deleted file mode 100644 index c017d591e..000000000 --- a/doc/modules/nvx-activation.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - -nvx::activation - - - -nvx::activation->nvx::activation::codec - - - - - -nvx::activation->nvx::activation::ready - - - - - -nvx::activation - - -nvx::activation - - - - - -nvx::activation::codec - - -codec - - - - - -nvx::activation::ready - - -ready - - - - - diff --git a/doc/modules/nvx-gpt.svg b/doc/modules/nvx-gpt.svg deleted file mode 100644 index fc886005f..000000000 --- a/doc/modules/nvx-gpt.svg +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -nvx::gpt - - - -nvx::gpt->nvx::gpt::crc - - - - - -nvx::gpt->nvx::gpt::entry - - - - - -nvx::gpt->nvx::gpt::error - - - - - -nvx::gpt->nvx::gpt::header - - - - - -nvx::gpt->nvx::gpt::source - - - - - -nvx::gpt - - -nvx::gpt - - - - - -nvx::gpt::crc - - -crc - - - - - -nvx::gpt::entry - - -entry - - - - - -nvx::gpt::error - - -error - - - - - -nvx::gpt::header - - -header - - - - - -nvx::gpt::source - - -source - - - - - diff --git a/doc/modules/nvx-guest.svg b/doc/modules/nvx-guest.svg index abf08c2aa..ed32cb6d2 100644 --- a/doc/modules/nvx-guest.svg +++ b/doc/modules/nvx-guest.svg @@ -4,1477 +4,123 @@ - - + + nvx::guest - - + + -nvx::guest->nvx::guest::cgroup - - - - - -nvx::guest->nvx::guest::clone - - - - - -nvx::guest->nvx::guest::console_pending - - - - - -nvx::guest->nvx::guest::device_io - - - - - -nvx::guest->nvx::guest::entry - - - - - -nvx::guest->nvx::guest::exec - - - - - -nvx::guest->nvx::guest::exit - - - - - -nvx::guest->nvx::guest::generation - - - - - -nvx::guest->nvx::guest::gpt - - - - - -nvx::guest->nvx::guest::ingress - - - - - -nvx::guest->nvx::guest::kernel_text - - - - - -nvx::guest->nvx::guest::managed_agent - - +nvx::guest->nvx::guest::activation + + - + nvx::guest->nvx::guest::mappings - - - - - -nvx::guest->nvx::guest::mmio_write - - + + - + nvx::guest->nvx::guest::nvxs - - - - - -nvx::guest->nvx::guest::parse - - - - - -nvx::guest->nvx::guest::process - - + + - + nvx::guest->nvx::guest::reseed - - - - - -nvx::guest->nvx::guest::root - - - - - -nvx::guest->nvx::guest::spawn - - + + - - -nvx::guest->nvx::guest::status - - - - - -nvx::guest->nvx::guest::sys - - + + +nvx::guest->nvx::guest::snapshot + + - + nvx::guest->nvx::guest::terminal - - + + - + nvx::guest->nvx::guest::time - - - - - -nvx::guest->nvx::guest::time_probe - - - - - -nvx::guest::clone->nvx::guest::clone::activation - - - - - -nvx::guest::clone->nvx::guest::clone::mounts - - - - - -nvx::guest::clone->nvx::guest::clone::repair - - - - - -nvx::guest::clone::activation->nvx::guest::clone::activation::apply - - - - - -nvx::guest::exec->nvx::guest::exec::binding - - - - - -nvx::guest::ingress->nvx::guest::ingress::address - - - - - -nvx::guest::ingress->nvx::guest::ingress::announcer - - - - - -nvx::guest::ingress->nvx::guest::ingress::collect - - - - - -nvx::guest::ingress->nvx::guest::ingress::diag - - - - - -nvx::guest::ingress->nvx::guest::ingress::encode - - - - - -nvx::guest::ingress->nvx::guest::ingress::sender - - - - - -nvx::guest::ingress->nvx::guest::ingress::table - - - - - -nvx::guest::ingress->nvx::guest::ingress::test_mode - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::cgroup - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::diagnostics - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::dispatch - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::environment - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::exec - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::exec_config - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::helper - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::launch - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::mappings - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::seam - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::session - - - - - -nvx::guest::managed_agent->nvx::guest::managed_agent::tty - - - - - -nvx::guest::mappings->nvx::guest::mappings::kernel - - - - - -nvx::guest::mappings->nvx::guest::mappings::mapper - - - - - -nvx::guest::mappings->nvx::guest::mappings::table - - - - - -nvx::guest::root->nvx::guest::root::block - - - - - -nvx::guest::root->nvx::guest::root::bootstrap - - - - - -nvx::guest::root->nvx::guest::root::kernel - - - - - -nvx::guest::root->nvx::guest::root::share - - - - - -nvx::guest::sys->nvx::guest::sys::clock - - - - - -nvx::guest::sys->nvx::guest::sys::discipline - - - - - -nvx::guest::sys->nvx::guest::sys::entry - - - - - -nvx::guest::sys->nvx::guest::sys::fd - - - - - -nvx::guest::sys->nvx::guest::sys::filesystem - - - - - -nvx::guest::sys->nvx::guest::sys::ioctl - - - - - -nvx::guest::sys->nvx::guest::sys::kernel - - - - - -nvx::guest::sys->nvx::guest::sys::mapping - - - - - -nvx::guest::sys->nvx::guest::sys::msr - - - - - -nvx::guest::sys->nvx::guest::sys::poll - - - - - -nvx::guest::sys->nvx::guest::sys::port - - - - - -nvx::guest::sys->nvx::guest::sys::process - - - - - -nvx::guest::sys->nvx::guest::sys::rooted - - - - - -nvx::guest::sys->nvx::guest::sys::scheduler - - - - - -nvx::guest::sys->nvx::guest::sys::secret - - - - - -nvx::guest::sys->nvx::guest::sys::signal - - - - - -nvx::guest::sys->nvx::guest::sys::sock_diag - - - - - -nvx::guest::sys->nvx::guest::sys::socket - - - - - -nvx::guest::sys->nvx::guest::sys::spawn - - - - - -nvx::guest::sys->nvx::guest::sys::terminal - - - - - -nvx::guest::sys->nvx::guest::sys::timer - - - - - -nvx::guest::sys->nvx::guest::sys::x86 - - - - - -nvx::guest::sys::port->nvx::guest::sys::port::access - - - - - -nvx::guest::sys::x86->nvx::guest::sys::x86::cpuid - - - - - -nvx::guest::sys::x86->nvx::guest::sys::x86::msr - - - - - -nvx::guest::time->nvx::guest::time::api - - - - - -nvx::guest::time->nvx::guest::time::checks - - - - - -nvx::guest::time->nvx::guest::time::commands - - - - - -nvx::guest::time->nvx::guest::time::cpu_mask - - - - - -nvx::guest::time->nvx::guest::time::daemon - - - - - -nvx::guest::time->nvx::guest::time::decoders - - - - - -nvx::guest::time->nvx::guest::time::events - - - - - -nvx::guest::time->nvx::guest::time::state - - - - - -nvx::guest::time->nvx::guest::time::system - - - - - -nvx::guest::time->nvx::guest::time::test_mode - - - - - -nvx::guest::time_probe->nvx::guest::time_probe::clock - - - - - -nvx::guest::time_probe->nvx::guest::time_probe::format - - - - - -nvx::guest::time_probe->nvx::guest::time_probe::latency - - - - - -nvx::guest::time_probe->nvx::guest::time_probe::support - - - - - -nvx::guest::time_probe->nvx::guest::time_probe::warp - - + + nvx::guest - -nvx::guest -Linux only + +nvx::guest +Linux only - + -nvx::guest::cgroup - - -cgroup - - - - - -nvx::guest::clone - - -clone - - - - - -nvx::guest::clone::activation - - -activation - - - - - -nvx::guest::clone::activation::apply - - -apply - - - - - -nvx::guest::clone::mounts - - -mounts - - - - - -nvx::guest::clone::repair - - -repair - - - - - -nvx::guest::console_pending - - -console_pending - - - - - -nvx::guest::device_io - - -device_io - - - - - -nvx::guest::entry - - -entry - - - - - -nvx::guest::exec - - -exec - - - - - -nvx::guest::exec::binding - - -binding - - - - - -nvx::guest::exit - - -exit - - - - - -nvx::guest::generation - - -generation - - - - - -nvx::guest::gpt - - -gpt - - - - - -nvx::guest::ingress - - -ingress - - - - - -nvx::guest::ingress::address - - -address - - - - - -nvx::guest::ingress::announcer - - -announcer - - - - - -nvx::guest::ingress::collect - - -collect - - - - - -nvx::guest::ingress::diag - - -diag - - - - - -nvx::guest::ingress::encode - - -encode - - - - - -nvx::guest::ingress::sender - - -sender - - - - - -nvx::guest::ingress::table - - -table - - - - - -nvx::guest::ingress::test_mode - - -test_mode - - - - - -nvx::guest::kernel_text - - -kernel_text - - - - - -nvx::guest::managed_agent - - -managed_agent - - - - - -nvx::guest::managed_agent::cgroup - - -cgroup - - - - - -nvx::guest::managed_agent::diagnostics - - -diagnostics - - - - - -nvx::guest::managed_agent::dispatch - - -dispatch - - - - - -nvx::guest::managed_agent::environment - - -environment - - - - - -nvx::guest::managed_agent::exec - - -exec - - - - - -nvx::guest::managed_agent::exec_config - - -exec_config - - - - - -nvx::guest::managed_agent::helper - - -helper - - - - - -nvx::guest::managed_agent::launch - - -launch - - - - - -nvx::guest::managed_agent::mappings - - -mappings - - - - - -nvx::guest::managed_agent::seam - - -seam - - - - - -nvx::guest::managed_agent::session - - -session - - - - - -nvx::guest::managed_agent::tty - - -tty +nvx::guest::activation + + +activation - + nvx::guest::mappings - - -mappings - - - - - -nvx::guest::mappings::kernel - - -kernel - - - - - -nvx::guest::mappings::mapper - - -mapper - - - - - -nvx::guest::mappings::table - - -table - - - - - -nvx::guest::mmio_write - - -mmio_write + + +mappings - + nvx::guest::nvxs - - -nvxs - - - - - -nvx::guest::parse - - -parse - - - - - -nvx::guest::process - - -process + + +nvxs - + nvx::guest::reseed - - -reseed - - - - - -nvx::guest::root - - -root - - - - - -nvx::guest::root::block - - -block - - - - - -nvx::guest::root::bootstrap - - -bootstrap - - - - - -nvx::guest::root::kernel - - -kernel - - - - - -nvx::guest::root::share - - -share - - - - - -nvx::guest::spawn - - -spawn - - - - - -nvx::guest::status - - -status - - - - - -nvx::guest::sys - - -sys - - - - - -nvx::guest::sys::clock - - -clock - - - - - -nvx::guest::sys::discipline - - -discipline - - - - - -nvx::guest::sys::entry - - -entry - - - - - -nvx::guest::sys::fd - - -fd - - - - - -nvx::guest::sys::filesystem - - -filesystem - - - - - -nvx::guest::sys::ioctl - - -ioctl - - - - - -nvx::guest::sys::kernel - - -kernel -pub(crate) - - - - - -nvx::guest::sys::mapping - - -mapping - - - - - -nvx::guest::sys::msr - - -msr + + +reseed - - -nvx::guest::sys::poll - - -poll - - - - - -nvx::guest::sys::port - - -port - - - - - -nvx::guest::sys::port::access - - -access - - - - - -nvx::guest::sys::process - - -process - - - - - -nvx::guest::sys::rooted - - -rooted - - - - - -nvx::guest::sys::scheduler - - -scheduler - - - - - -nvx::guest::sys::secret - - -secret - - - - - -nvx::guest::sys::signal - - -signal - - - - - -nvx::guest::sys::sock_diag - - -sock_diag - - - - - -nvx::guest::sys::socket - - -socket - - - - - -nvx::guest::sys::spawn - - -spawn - - - - - -nvx::guest::sys::terminal - - -terminal - - - - - -nvx::guest::sys::timer - - -timer - - - - - -nvx::guest::sys::x86 - - -x86 -pub(crate) - - - - - -nvx::guest::sys::x86::cpuid - - -cpuid -pub(crate) - - - - - -nvx::guest::sys::x86::msr - - -msr -pub(crate) + + +nvx::guest::snapshot + + +snapshot - + nvx::guest::terminal - - -terminal + + +terminal - + nvx::guest::time - - -time - - - - - -nvx::guest::time::api - - -api - - - - - -nvx::guest::time::checks - - -checks - - - - - -nvx::guest::time::commands - - -commands - - - - - -nvx::guest::time::cpu_mask - - -cpu_mask - - - - - -nvx::guest::time::daemon - - -daemon - - - - - -nvx::guest::time::decoders - - -decoders - - - - - -nvx::guest::time::events - - -events - - - - - -nvx::guest::time::state - - -state - - - - - -nvx::guest::time::system - - -system - - - - - -nvx::guest::time::test_mode - - -test_mode - - - - - -nvx::guest::time_probe - - -time_probe - - - - - -nvx::guest::time_probe::clock - - -clock - - - - - -nvx::guest::time_probe::format - - -format - - - - - -nvx::guest::time_probe::latency - - -latency - - - - - -nvx::guest::time_probe::support - - -support - - - - - -nvx::guest::time_probe::warp - - -warp + + +time diff --git a/doc/modules/nvx-host.svg b/doc/modules/nvx-host.svg index 4aa626bf7..4972e0628 100644 --- a/doc/modules/nvx-host.svg +++ b/doc/modules/nvx-host.svg @@ -4,2054 +4,182 @@ - - + + nvx::host - - + + -nvx::host->nvx::host::artifacts - - - - - -nvx::host->nvx::host::cli - - +nvx::host->nvx::host::config + + - + nvx::host->nvx::host::filesystem - - - - - -nvx::host->nvx::host::instance - - - - - -nvx::host->nvx::host::launch - - + + - + nvx::host->nvx::host::network - - + + - + nvx::host->nvx::host::nvxs - - - - - -nvx::host->nvx::host::paths - - - - - -nvx::host->nvx::host::platform - - + + - + nvx::host->nvx::host::process - - + + - + nvx::host->nvx::host::report - - - - - -nvx::host->nvx::host::sandbox - - - - - -nvx::host->nvx::host::seal - - - - - -nvx::host->nvx::host::session - - - - - -nvx::host->nvx::host::spawn - - - - - -nvx::host->nvx::host::store - - + + - - -nvx::host->nvx::host::supervisor - - - - - -nvx::host->nvx::host::sys - - - - - -nvx::host->nvx::host::template - - - - - -nvx::host::artifacts->nvx::host::artifacts::dir - - - - - -nvx::host::artifacts->nvx::host::artifacts::guest - - - - - -nvx::host::artifacts->nvx::host::artifacts::identity - - - - - -nvx::host::artifacts->nvx::host::artifacts::require - - - - + -nvx::host::cli->nvx::host::cli::args - - +nvx::host->nvx::host::restore + + - + -nvx::host::cli->nvx::host::cli::artifacts - - +nvx::host->nvx::host::runtime + + - + -nvx::host::cli->nvx::host::cli::cpu_profile - - +nvx::host->nvx::host::snapshot + + - + -nvx::host::cli->nvx::host::cli::deadline - - +nvx::host->nvx::host::transport + + - + -nvx::host::cli->nvx::host::cli::download - - - - - -nvx::host::cli->nvx::host::cli::egress - - - - - -nvx::host::cli->nvx::host::cli::json - - - - - -nvx::host::cli->nvx::host::cli::network - - - - - -nvx::host::cli->nvx::host::cli::output - - - - - -nvx::host::cli->nvx::host::cli::run - - - - - -nvx::host::cli->nvx::host::cli::sandbox - - - - - -nvx::host::cli->nvx::host::cli::session - - - - - -nvx::host::cli->nvx::host::cli::shell - - - - - -nvx::host::cli->nvx::host::cli::store - - - - - -nvx::host::cli::download->nvx::host::cli::download::archive - - - - - -nvx::host::cli::download->nvx::host::cli::download::git - - - - - -nvx::host::cli::download->nvx::host::cli::download::github - - - - - -nvx::host::cli::download->nvx::host::cli::download::install - - - - - -nvx::host::cli::sandbox->nvx::host::cli::sandbox::lifecycle - - - - - -nvx::host::cli::sandbox->nvx::host::cli::sandbox::lock - - - - - -nvx::host::cli::sandbox->nvx::host::cli::sandbox::one_shot - - - - - -nvx::host::cli::sandbox->nvx::host::cli::sandbox::options - - - - - -nvx::host::cli::sandbox->nvx::host::cli::sandbox::state - - - - - -nvx::host::cli::sandbox::lifecycle->nvx::host::cli::sandbox::lifecycle::directory - - - - - -nvx::host::cli::sandbox::lifecycle->nvx::host::cli::sandbox::lifecycle::outcome - - - - - -nvx::host::cli::sandbox::lifecycle->nvx::host::cli::sandbox::lifecycle::start - - - - - -nvx::host::cli::sandbox::lifecycle->nvx::host::cli::sandbox::lifecycle::stop - - - - - -nvx::host::cli::store->nvx::host::cli::store::locked - - - - - -nvx::host::cli::store->nvx::host::cli::store::names - - - - - -nvx::host::filesystem->nvx::host::filesystem::guest_path - - - - - -nvx::host::filesystem->nvx::host::filesystem::host_path - - - - - -nvx::host::filesystem->nvx::host::filesystem::limits - - - - - -nvx::host::filesystem->nvx::host::filesystem::policy - - - - - -nvx::host::filesystem->nvx::host::filesystem::verify - - - - - -nvx::host::instance->nvx::host::instance::execution - - - - - -nvx::host::instance->nvx::host::instance::files - - - - - -nvx::host::instance->nvx::host::instance::locked - - - - - -nvx::host::network->nvx::host::network::address - - - - - -nvx::host::network->nvx::host::network::arguments - - - - - -nvx::host::network->nvx::host::network::loopback - - - - - -nvx::host::network->nvx::host::network::lowering - - - - - -nvx::host::network->nvx::host::network::policy - - - - - -nvx::host::network->nvx::host::network::proxy - - - - - -nvx::host::network->nvx::host::network::rule - - - - - -nvx::host::network::policy->nvx::host::network::policy::json - - - - - -nvx::host::platform->nvx::host::platform::inherit - - - - - -nvx::host::platform->nvx::host::platform::linux - - - - - -nvx::host::platform->nvx::host::platform::windows - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::endpoint - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::identity - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::log_claim - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::private - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::process - - - - - -nvx::host::platform::linux->nvx::host::platform::linux::spawn - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::endpoint - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::identity - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::interrupt - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::log_claim - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::private - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::process - - - - - -nvx::host::platform::windows->nvx::host::platform::windows::spawn - - - - - -nvx::host::sandbox->nvx::host::sandbox::distro - - - - - -nvx::host::sandbox->nvx::host::sandbox::geometry - - - - - -nvx::host::sandbox->nvx::host::sandbox::layer - - - - - -nvx::host::sandbox->nvx::host::sandbox::mount - - - - - -nvx::host::sandbox->nvx::host::sandbox::options - - - - - -nvx::host::sandbox->nvx::host::sandbox::plan - - - - - -nvx::host::sandbox->nvx::host::sandbox::prepared - - - - - -nvx::host::sandbox->nvx::host::sandbox::scratch - - - - - -nvx::host::sandbox::mount->nvx::host::sandbox::mount::aggregate - - - - - -nvx::host::sandbox::mount->nvx::host::sandbox::mount::boot - - - - - -nvx::host::sandbox::mount->nvx::host::sandbox::mount::host_paths - - - - - -nvx::host::sandbox::mount->nvx::host::sandbox::mount::request - - - - - -nvx::host::sandbox::plan->nvx::host::sandbox::plan::form - - - - - -nvx::host::sandbox::scratch->nvx::host::sandbox::scratch::imp - - - - - -nvx::host::seal->nvx::host::seal::imp - - - - - -nvx::host::seal->nvx::host::seal::windows - - - - - -nvx::host::store->nvx::host::store::caller - - - - - -nvx::host::store->nvx::host::store::files - - - - - -nvx::host::store->nvx::host::store::identity - - - - - -nvx::host::store->nvx::host::store::launch - - - - - -nvx::host::store->nvx::host::store::lease - - - - - -nvx::host::store->nvx::host::store::lifecycle - - - - - -nvx::host::store->nvx::host::store::locked - - - - - -nvx::host::store->nvx::host::store::names - - - - - -nvx::host::store->nvx::host::store::record - - - - - -nvx::host::store->nvx::host::store::regular - - - - - -nvx::host::store->nvx::host::store::vmm - - - - - -nvx::host::supervisor->nvx::host::supervisor::launch - - - - - -nvx::host::supervisor->nvx::host::supervisor::reattach - - - - - -nvx::host::supervisor->nvx::host::supervisor::vmm - - - - - -nvx::host::sys->nvx::host::sys::fs - - - - - -nvx::host::sys->nvx::host::sys::interrupt - - - - - -nvx::host::sys->nvx::host::sys::process - - - - - -nvx::host::sys->nvx::host::sys::socket - - - - - -nvx::host::template->nvx::host::template::canonical - - - - - -nvx::host::template->nvx::host::template::clone - - - - - -nvx::host::template->nvx::host::template::eligibility - - - - - -nvx::host::template->nvx::host::template::key - - - - - -nvx::host::template->nvx::host::template::manifest - - - - - -nvx::host::template->nvx::host::template::plan - - - - - -nvx::host::template->nvx::host::template::store - - - - - -nvx::host::template->nvx::host::template::wire - - - - - -nvx::host::template::clone->nvx::host::template::clone::export - - - - - -nvx::host::template::clone->nvx::host::template::clone::failure - - - - - -nvx::host::template::clone->nvx::host::template::clone::launch - - - - - -nvx::host::template::clone->nvx::host::template::clone::reattach - - - - - -nvx::host::template::clone->nvx::host::template::clone::record - - - - - -nvx::host::template::clone->nvx::host::template::clone::records - - - - - -nvx::host::template::clone->nvx::host::template::clone::restore - - - - - -nvx::host::template::store->nvx::host::template::store::capture - - - - - -nvx::host::template::store->nvx::host::template::store::leases - - +nvx::host->nvx::host::vm + + nvx::host - -nvx::host + +nvx::host - + -nvx::host::artifacts - - -artifacts +nvx::host::config + + +config - + -nvx::host::artifacts::dir - - -dir +nvx::host::filesystem + + +filesystem - + -nvx::host::artifacts::guest - - -guest +nvx::host::network + + +network - + -nvx::host::artifacts::identity - - -identity +nvx::host::nvxs + + +nvxs - + -nvx::host::artifacts::require - - -require +nvx::host::process + + +process - + -nvx::host::cli - - -cli +nvx::host::report + + +report - + -nvx::host::cli::args - - -args +nvx::host::restore + + +restore - + -nvx::host::cli::artifacts - - -artifacts +nvx::host::runtime + + +runtime - + -nvx::host::cli::cpu_profile - - -cpu_profile +nvx::host::snapshot + + +snapshot - + -nvx::host::cli::deadline - - -deadline +nvx::host::transport + + +transport - + -nvx::host::cli::download - - -download - - - - - -nvx::host::cli::download::archive - - -archive - - - - - -nvx::host::cli::download::git - - -git - - - - - -nvx::host::cli::download::github - - -github - - - - - -nvx::host::cli::download::install - - -install - - - - - -nvx::host::cli::egress - - -egress - - - - - -nvx::host::cli::json - - -json - - - - - -nvx::host::cli::network - - -network - - - - - -nvx::host::cli::output - - -output - - - - - -nvx::host::cli::run - - -run - - - - - -nvx::host::cli::sandbox - - -sandbox - - - - - -nvx::host::cli::sandbox::lifecycle - - -lifecycle - - - - - -nvx::host::cli::sandbox::lifecycle::directory - - -directory - - - - - -nvx::host::cli::sandbox::lifecycle::outcome - - -outcome - - - - - -nvx::host::cli::sandbox::lifecycle::start - - -start - - - - - -nvx::host::cli::sandbox::lifecycle::stop - - -stop - - - - - -nvx::host::cli::sandbox::lock - - -lock - - - - - -nvx::host::cli::sandbox::one_shot - - -one_shot - - - - - -nvx::host::cli::sandbox::options - - -options - - - - - -nvx::host::cli::sandbox::state - - -state - - - - - -nvx::host::cli::session - - -session - - - - - -nvx::host::cli::shell - - -shell - - - - - -nvx::host::cli::store - - -store - - - - - -nvx::host::cli::store::locked - - -locked - - - - - -nvx::host::cli::store::names - - -names - - - - - -nvx::host::filesystem - - -filesystem - - - - - -nvx::host::filesystem::guest_path - - -guest_path - - - - - -nvx::host::filesystem::host_path - - -host_path - - - - - -nvx::host::filesystem::limits - - -limits - - - - - -nvx::host::filesystem::policy - - -policy - - - - - -nvx::host::filesystem::verify - - -verify - - - - - -nvx::host::instance - - -instance - - - - - -nvx::host::instance::execution - - -execution - - - - - -nvx::host::instance::files - - -files - - - - - -nvx::host::instance::locked - - -locked - - - - - -nvx::host::launch - - -launch - - - - - -nvx::host::network - - -network - - - - - -nvx::host::network::address - - -address - - - - - -nvx::host::network::arguments - - -arguments - - - - - -nvx::host::network::loopback - - -loopback - - - - - -nvx::host::network::lowering - - -lowering - - - - - -nvx::host::network::policy - - -policy - - - - - -nvx::host::network::policy::json - - -json -pub(crate) - - - - - -nvx::host::network::proxy - - -proxy - - - - - -nvx::host::network::rule - - -rule - - - - - -nvx::host::nvxs - - -nvxs - - - - - -nvx::host::paths - - -paths -pub(crate) - - - - - -nvx::host::platform - - -platform - - - - - -nvx::host::platform::inherit - - -inherit -pub(crate) - - - - - -nvx::host::platform::linux - - -linux -Linux only - - - - - -nvx::host::platform::linux::endpoint - - -endpoint - - - - - -nvx::host::platform::linux::identity - - -identity - - - - - -nvx::host::platform::linux::log_claim - - -log_claim - - - - - -nvx::host::platform::linux::private - - -private - - - - - -nvx::host::platform::linux::process - - -process - - - - - -nvx::host::platform::linux::spawn - - -spawn - - - - - -nvx::host::platform::windows - - -windows -Windows only - - - - - -nvx::host::platform::windows::endpoint - - -endpoint - - - - - -nvx::host::platform::windows::identity - - -identity - - - - - -nvx::host::platform::windows::interrupt - - -interrupt - - - - - -nvx::host::platform::windows::log_claim - - -log_claim - - - - - -nvx::host::platform::windows::private - - -private - - - - - -nvx::host::platform::windows::process - - -process - - - - - -nvx::host::platform::windows::spawn - - -spawn - - - - - -nvx::host::process - - -process - - - - - -nvx::host::report - - -report - - - - - -nvx::host::sandbox - - -sandbox - - - - - -nvx::host::sandbox::distro - - -distro - - - - - -nvx::host::sandbox::geometry - - -geometry -pub(crate) - - - - - -nvx::host::sandbox::layer - - -layer - - - - - -nvx::host::sandbox::mount - - -mount - - - - - -nvx::host::sandbox::mount::aggregate - - -aggregate - - - - - -nvx::host::sandbox::mount::boot - - -boot - - - - - -nvx::host::sandbox::mount::host_paths - - -host_paths - - - - - -nvx::host::sandbox::mount::request - - -request - - - - - -nvx::host::sandbox::options - - -options - - - - - -nvx::host::sandbox::plan - - -plan - - - - - -nvx::host::sandbox::plan::form - - -form - - - - - -nvx::host::sandbox::prepared - - -prepared - - - - - -nvx::host::sandbox::scratch - - -scratch - - - - - -nvx::host::sandbox::scratch::imp - - -imp - - - - - -nvx::host::seal - - -seal - - - - - -nvx::host::seal::imp - - -imp -Linux only - - - - - -nvx::host::seal::windows - - -windows -Windows only - - - - - -nvx::host::session - - -session - - - - - -nvx::host::spawn - - -spawn - - - - - -nvx::host::store - - -store - - - - - -nvx::host::store::caller - - -caller - - - - - -nvx::host::store::files - - -files -pub(crate) - - - - - -nvx::host::store::identity - - -identity - - - - - -nvx::host::store::launch - - -launch - - - - - -nvx::host::store::lease - - -lease -pub(crate) - - - - - -nvx::host::store::lifecycle - - -lifecycle - - - - - -nvx::host::store::locked - - -locked - - - - - -nvx::host::store::names - - -names - - - - - -nvx::host::store::record - - -record - - - - - -nvx::host::store::regular - - -regular -pub(crate) - - - - - -nvx::host::store::vmm - - -vmm - - - - - -nvx::host::supervisor - - -supervisor - - - - - -nvx::host::supervisor::launch - - -launch - - - - - -nvx::host::supervisor::reattach - - -reattach - - - - - -nvx::host::supervisor::vmm - - -vmm - - - - - -nvx::host::sys - - -sys -Linux only - - - - - -nvx::host::sys::fs - - -fs - - - - - -nvx::host::sys::interrupt - - -interrupt - - - - - -nvx::host::sys::process - - -process - - - - - -nvx::host::sys::socket - - -socket - - - - - -nvx::host::template - - -template - - - - - -nvx::host::template::canonical - - -canonical - - - - - -nvx::host::template::clone - - -clone - - - - - -nvx::host::template::clone::export - - -export - - - - - -nvx::host::template::clone::failure - - -failure - - - - - -nvx::host::template::clone::launch - - -launch - - - - - -nvx::host::template::clone::reattach - - -reattach - - - - - -nvx::host::template::clone::record - - -record - - - - - -nvx::host::template::clone::records - - -records - - - - - -nvx::host::template::clone::restore - - -restore - - - - - -nvx::host::template::eligibility - - -eligibility - - - - - -nvx::host::template::key - - -key - - - - - -nvx::host::template::manifest - - -manifest - - - - - -nvx::host::template::plan - - -plan - - - - - -nvx::host::template::store - - -store - - - - - -nvx::host::template::store::capture - - -capture - - - - - -nvx::host::template::store::leases - - -leases - - - - - -nvx::host::template::wire - - -wire +nvx::host::vm + + +vm diff --git a/doc/modules/nvx-protocol.svg b/doc/modules/nvx-protocol.svg index 27271acec..8aa5adedd 100644 --- a/doc/modules/nvx-protocol.svg +++ b/doc/modules/nvx-protocol.svg @@ -4,242 +4,152 @@ - - + + nvx::protocol - - - -nvx::protocol->nvx::protocol::app - - - - - -nvx::protocol->nvx::protocol::boot - - - + - + nvx::protocol->nvx::protocol::cmdline - - - - - -nvx::protocol->nvx::protocol::exec - - + + - + nvx::protocol->nvx::protocol::export - - - - - -nvx::protocol->nvx::protocol::features - - + + - + nvx::protocol->nvx::protocol::maps - - + + - + nvx::protocol->nvx::protocol::nvxs - - + + - - -nvx::protocol->nvx::protocol::outer - - + + +nvx::protocol->nvx::protocol::restore_ready + + - + nvx::protocol->nvx::protocol::root - - + + - + nvx::protocol->nvx::protocol::share - - + + - + nvx::protocol->nvx::protocol::status - - + + - + -nvx::protocol::nvxs->nvx::protocol::nvxs::guest - - - - - -nvx::protocol::nvxs->nvx::protocol::nvxs::host - - - - - -nvx::protocol::nvxs->nvx::protocol::nvxs::record - - +nvx::protocol->nvx::protocol::view + + nvx::protocol - -nvx::protocol - - - - - -nvx::protocol::app - - -app - - - - - -nvx::protocol::boot - - -boot + +nvx::protocol - + nvx::protocol::cmdline - - -cmdline - - - - - -nvx::protocol::exec - - -exec + + +cmdline - + nvx::protocol::export - - -export - - - - - -nvx::protocol::features - - -features + + +export - + nvx::protocol::maps - - -maps + + +maps - + nvx::protocol::nvxs - - -nvxs + + +nvxs - - -nvx::protocol::nvxs::guest - - -guest - - - - - -nvx::protocol::nvxs::host - - -host - - - - - -nvx::protocol::nvxs::record - - -record - - - - - -nvx::protocol::outer - - -outer + + +nvx::protocol::restore_ready + + +restore_ready - + nvx::protocol::root - - -root + + +root - + nvx::protocol::share - - -share + + +share - + nvx::protocol::status - - -status + + +status + + + + + +nvx::protocol::view + + +view diff --git a/doc/modules/nvx-testing.svg b/doc/modules/nvx-testing.svg deleted file mode 100644 index b4db434ff..000000000 --- a/doc/modules/nvx-testing.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - -nvx::testing - - - -nvx::testing->nvx::testing::fake_openvmm - - - - - -nvx::testing - - -nvx::testing - - - - - -nvx::testing::fake_openvmm - - -fake_openvmm - - - - - diff --git a/doc/modules/overview.svg b/doc/modules/overview.svg index cffff545c..ce7f00969 100644 --- a/doc/modules/overview.svg +++ b/doc/modules/overview.svg @@ -4,41 +4,33 @@ - - + + nvx - + cluster_nvx - -nvx + +nvx - - - - - - + + + - - - - - - + + + - - + nvx::activation - -activation -Layered root activation -2 nested modules + +activation +Layered root activation @@ -46,119 +38,74 @@ nvx::contract - -contract -Control compatibility - - - - - -nvx::error - - -error -Error types -pub(crate) - - - - - -nvx::exec - - -exec -Execution stdio and credit -6 nested modules + +contract +Contract versions - + nvx::gpt - - -gpt -GPT partition reader -5 nested modules + + +gpt +GPT partition reader - + nvx::guest - - -guest -Linux guest programs -97 nested modules -Linux only + + +guest +Guest side +7 nested modules +Linux only - + nvx::host - - -host -Launch and supervision -135 nested modules - - - - - -nvx::hypervisor - - -hypervisor -Backend selection -pub(crate) + + +host +VMs, snapshots, restores +11 nested modules - + nvx::net - - -net -IP networks -1 nested module + + +net +IP networks +1 nested module - + nvx::protocol - - -protocol -NVXS / NVXC codecs -15 nested modules - - - - - -nvx::testing - - -testing -Fake OpenVMM -1 nested module + + +protocol +NVXS and boot tokens +9 nested modules - + nvx::uuid - - -uuid -UUID encoding + + +uuid +UUID encoding diff --git a/doc/project-structure.md b/doc/project-structure.md index d3ae54ec6..7c0377028 100644 --- a/doc/project-structure.md +++ b/doc/project-structure.md @@ -45,7 +45,7 @@ nvx/ | | |-- ci.yml Main build, test, benchmark, and release workflow | | |-- copilot-code-review.yml Copilot code review environment | | |-- copilot-setup-steps.yml Copilot cloud agent environment -| | |-- module-diagrams.yml Module diagrams of the nvx crate, drawn after merges +| | |-- module-diagrams.yml Module diagrams of the nvx crate, drawn every night | | |-- nightly-ci-report.yml Report of failed nightly CI runs | | |-- nvx-rs-mutants.yml On-demand mutation testing of nvx-rs | | |-- package-platform.yml Reusable platform release package job @@ -226,7 +226,7 @@ reusable workflows: `build-openvmm-binary.yml` builds OpenVMM for one target, also publish the `nvx` crate to crates.io. The other workflows report failed nightly CI runs, flag the pull requests that change existing tests, draw the module -diagrams of the `nvx` crate after merges, run adversarial campaigns, run the +diagrams of the `nvx` crate every night, run adversarial campaigns, run the `nvx` crate's mutation tests, verify published releases with Specula, and prepare the Copilot cloud agent and code review environments. See diff --git a/nvx-rs/src/contract.rs b/nvx-rs/src/contract.rs index b0beaaaa1..224abbfac 100644 --- a/nvx-rs/src/contract.rs +++ b/nvx-rs/src/contract.rs @@ -1,11 +1,11 @@ //! Versions of the contracts that this crate speaks with OpenVMM and the NVX guest, and the //! compatibility of NVX releases with them. //! -//! The store records the boot contract, control contract, and control session versions of the -//! build that started a VM in the VM's runtime identity, `nvx::host::runtime::RuntimeIdentity`, -//! and a build drives only the VMs whose runtime identity is its own. So each version changes -//! whenever a guest that a build of the earlier version started could not be driven as one that a -//! build of the later version starts: +//! A VM's runtime identity, `nvx::host::runtime::RuntimeIdentity`, which its caller saves with +//! it, holds the boot contract, control contract, and control session versions of the build that +//! started the VM, and a build drives only the VMs whose runtime identity is its own. So each +//! version changes whenever a guest that a build of the earlier version started could not be +//! driven as one that a build of the later version starts: //! //! - [`MICROVM_ABI_VERSION`] is the microVM ABI of the pinned OpenVMM: the machine, its devices, //! and their addresses, through which OpenVMM boots the guest and saves its state. It changes @@ -22,13 +22,14 @@ //! host reaches the guest agent over OpenVMM's control console. It changes with the session's //! framing, records, credits, or authentication. //! - [`CONTROL_CONTRACT_REVISION`] is the revision of the control contract that the session -//! carries: the requests, responses, and features of the guest agent. It changes with them. +//! carries: the requests and responses of NVX's own control protocol, which NVX's guest agents +//! serve. It changes with them. //! //! A release declares the microVM ABI, time ABI, restore-packet, control-session, and control //! contract versions of its artifacts in its `SOURCE-MANIFEST.json`. //! -//! The compatibility key of a template, `nvx::host::snapshot::SnapshotKey`, holds the boot and -//! control versions, and the versions of the contracts through which a template's clones are +//! The compatibility key of a snapshot, `nvx::host::snapshot::SnapshotKey`, holds the boot and +//! control versions, and the versions of the contracts through which the snapshot's clones are //! restored, which no runtime identity records: //! //! - [`SNAPSHOT_FORMAT_VERSION`] is the version of OpenVMM's snapshot format: the manifest of a @@ -51,7 +52,7 @@ /// /// Version 1 of OpenVMM's v4 microVM offers no device `VIRTIO_F_ACCESS_PLATFORM` and no console /// `VIRTIO_CONSOLE_F_SIZE`, and gives each virtio device an IOAPIC input of its own past the ISA -/// IRQs. OpenVMM and this crate refuse the template of an earlier OpenVMM by its +/// IRQs. OpenVMM and this crate refuse the snapshot of an earlier OpenVMM by its /// [snapshot format](SNAPSHOT_FORMAT_VERSION). pub const MICROVM_ABI_VERSION: u64 = 1; /// Version of the boot contract this crate speaks; see the [module](self) for when it changes. diff --git a/nvx-rs/src/error.rs b/nvx-rs/src/error.rs index fa8877dfb..0e55a8acb 100644 --- a/nvx-rs/src/error.rs +++ b/nvx-rs/src/error.rs @@ -24,8 +24,8 @@ pub enum ErrorKind { /// A host I/O operation failed, or state that NVX recorded on the host is unreadable or /// malformed. Io, - /// The state that an operation names does not exist on the host, such as the store of a - /// sandbox that was never created or that a removal deleted. + /// The state that an operation names does not exist on the host, such as the manifest of a + /// capture that OpenVMM never wrote. NotFound, /// The control session failed: the peer violated the protocol, closed or reset the session, /// or refused a request. diff --git a/nvx-rs/src/guest/activation.rs b/nvx-rs/src/guest/activation.rs index f9722c8c9..966e19c2d 100644 --- a/nvx-rs/src/guest/activation.rs +++ b/nvx-rs/src/guest/activation.rs @@ -114,37 +114,13 @@ //! //! [guest-agent guidelines]: https://github.com/microsoft/nvx/blob/dev/doc/guest-agent-guidelines.md //! -//! Each adapter applies the record in its own space. nvx-init applies it with its own, NVX's -//! internal reference code, private to the crate, which activates the clone as at a cold boot. It -//! assembles the root as a cold boot does, with the record's image, an EROFS image -//! whose UUID it checks if the record expects one, or a GPT disk whose GUID it checks if the -//! record expects one, its upper, a tmpfs or the scratch, with the record's journal policy, and -//! its overlay mode, and with the inputs that the guest takes from its own configuration, which -//! its template captured: the runtime directory, the block devices, the host filesystem device's -//! virtio-fs tag, the gateway of the template's NIC, if it has one, and how long the assembly waits -//! for each block device, but never past the activation's deadline, so -//! that the application ends by then. For a GPT disk, it first asks the kernel to reread the disk's -//! partition table, once the distro device appeared, by the same deadline: the kernel created the -//! block devices of partitions from the template's placeholder, when it found it, and creates those -//! of the clone's disk then, from the same read-only bytes that the assembly reads and checks, as -//! at a cold boot. The upper must be fresh: the assembly refuses one whose `upper` or `work` -//! directory exists and is not empty, since a clone's root starts as its image alone, while a cold -//! sandbox's scratch keeps its upper across boots; the check reads at most one entry of each, and -//! no file. The record holds no execution identity: the adapter gives each execution the identity -//! that the execution names, as for a sandbox without a workload identity at a cold boot. If the -//! record has an export, it mounts the host export through the template's filesystem device, -//! read-write, at `hostfs/root` in the runtime directory, outside the root, where only the guest's -//! root user reaches it: the record names no mode, and OpenVMM limits each of the export's -//! children to its own access. It applies the record's VM-wide view, with the record's DNS server, -//! if any. The root owns the machine-ID and resolver binds, including any made before a later -//! failure. The view sets the VM's hostname and generates an ID only when the record requests -//! one, after the repair's reseed. The application starts no process. nvx-init keeps the root and -//! the workload that it activated, with the export's root, from which it maps the host's mapping -//! table once the clone is ready, the network's proxy endpoint, which it tells its workloads as at -//! a cold boot, and the publication and gateway with which it starts and supervises ingress. Their -//! teardown closes the export's root, then tears the root down with its view binds. An -//! application that fails leaves nothing that it mounted or bound mounted, unless its error holds -//! the unmounts that failed. +//! Each adapter applies the record in its own space, and the library applies none of it. +//! nvx-init applies it with NVX's own space, internal reference code private to the crate, as a +//! cold boot assembles its root, by the activation's deadline, and starts no process. The +//! guidelines give the interface of that space and the rules that it keeps, which another init's +//! space may adapt, under [Build the space]. +//! +//! [Build the space]: https://github.com/microsoft/nvx/blob/dev/doc/guest-agent-guidelines.md#build-the-space //! //! # Example //! @@ -200,14 +176,8 @@ //! report-only finish without a running time daemon, which runs the deferred time work itself, //! in a child that it waits for. They never power the VM off, so a caller that is not PID 1 keeps //! its own reaping, transport, and shutdown. The activation needs no privilege, and -//! makes no system call and changes no admission fence. nvx-init's -//! application of the record needs the guest's root user, in the guest's initial mount -//! namespace, as its assembly of a root does: for a GPT disk, it asks the kernel to reread the -//! disk's partition table, which needs `CAP_SYS_ADMIN`; it mounts the root, applies its VM-wide -//! view, and, if it fails, tears it down. It starts no process and generates at most one ID, a -//! machine ID that the record asks for. It mounts the host export of a record that has one, and -//! writes the machine ID's and the resolver's files in the runtime directory, whose binds over -//! the root's `/etc/machine-id` and `/etc/resolv.conf` need Linux 6.8 or later. +//! makes no system call and changes no admission fence: a record's application needs the +//! privileges of the adapter's own space. pub(crate) mod state; #[cfg(test)] @@ -246,7 +216,8 @@ const BLOCK_DEVICES: &str = "dev/block"; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum CaptureStage { - /// Closing the caller's fence of workload admission, which fails while it is closed already. + /// Closing a fence of workload admission, a stage that no capture has: the capture holds no + /// fence, and its caller keeps its own executions still. Fence, /// Checking that nothing is mounted from the template's devices or an overlay. Mounts, diff --git a/nvx-rs/src/guest/activation/state/apply.rs b/nvx-rs/src/guest/activation/state/apply.rs index 54e2694bf..8f2e07d3e 100644 --- a/nvx-rs/src/guest/activation/state/apply.rs +++ b/nvx-rs/src/guest/activation/state/apply.rs @@ -1,8 +1,47 @@ //! Application of an accepted activation record, without starting a process. //! //! This module is part of the guest's space, nvx-init's internal reference code, private to the -//! crate, as the [activation](crate::guest::activation#activation) describes. The root owns the -//! VM-wide view's binds; the caller owns ingress and the admission of its workloads. +//! crate, which applies the record that the [activation](crate::guest::activation#activation) +//! accepted, as the adapter's own space. The root owns the VM-wide view's binds; the caller owns +//! ingress and the admission of its workloads. +//! +//! It activates the clone as at a cold boot. It assembles the root as a cold boot does, with the +//! record's image, an EROFS image whose UUID it checks if the record expects one, or a GPT disk +//! whose GUID it checks if the record expects one, its upper, a tmpfs or the scratch, with the +//! record's journal policy, and its overlay mode, and with the inputs that the guest takes from +//! its own configuration, which its template captured, a [`RootTemplate`]: the runtime directory, +//! the block devices, the host filesystem device's virtio-fs tag, the gateway of the template's +//! NIC, if it has one, and how long the assembly waits for each block device, but never past the +//! activation's deadline, so that the application ends by then. For a GPT disk, it first asks the +//! kernel to reread the disk's partition table, once the distro device appeared, by the same +//! deadline: the kernel created the block devices of partitions from the template's placeholder, +//! when it found it, and creates those of the clone's disk then, from the same read-only bytes +//! that the assembly reads and checks, as at a cold boot. The upper must be fresh: the assembly +//! refuses one whose `upper` or `work` directory exists and is not empty, since a clone's root +//! starts as its image alone, while a cold sandbox's scratch keeps its upper across boots; the +//! check reads at most one entry of each, and no file. The record holds no execution identity: +//! the adapter gives each execution the identity that the execution names, as for a sandbox +//! without a workload identity at a cold boot. If the record has an export, it mounts the host +//! export through the template's filesystem device, read-write, at `hostfs/root` in the runtime +//! directory, outside the root, where only the guest's root user reaches it: the record names no +//! mode, and OpenVMM limits each of the export's children to its own access. It applies the +//! record's VM-wide view, with the record's DNS server, if any. The root owns the machine-ID and +//! resolver binds, including any made before a later failure. The view sets the VM's hostname +//! and generates an ID only when the record requests one, after the repair's reseed. The +//! application starts no process. nvx-init keeps the root and the workload that it activated, +//! with the export's root, from which it maps the host's mapping table once the clone is ready, +//! the network's proxy endpoint, which it tells its workloads as at a cold boot, and the +//! publication and gateway with which it starts and supervises ingress. Their teardown closes the +//! export's root, then tears the root down with its view binds. An application that fails leaves +//! nothing that it mounted or bound mounted, unless its error holds the unmounts that failed. +//! +//! The application needs the guest's root user, in the guest's initial mount namespace, as its +//! assembly of a root does: for a GPT disk, it asks the kernel to reread the disk's partition +//! table, which needs `CAP_SYS_ADMIN`; it mounts the root, applies its VM-wide view, and, if it +//! fails, tears it down. It starts no process and generates at most one ID, a machine ID that the +//! record asks for. It mounts the host export of a record that has one, and writes the machine +//! ID's and the resolver's files in the runtime directory, whose binds over the root's +//! `/etc/machine-id` and `/etc/resolv.conf` need Linux 6.8 or later. #[cfg(test)] mod tests; diff --git a/nvx-rs/src/guest/managed_agent/launch.rs b/nvx-rs/src/guest/managed_agent/launch.rs index 6ed21abdc..3b2740097 100644 --- a/nvx-rs/src/guest/managed_agent/launch.rs +++ b/nvx-rs/src/guest/managed_agent/launch.rs @@ -10,9 +10,10 @@ use super::environment; /// Describes one workload for the init-owned plain-process core. /// -/// The workload's standard input is the null device, its standard output and error are the -/// core's pipes. The plain-process core resets the child's priority and credentials, enters -/// its working directory, and executes the workload in its own process group. +/// The request reads the null device as standard input, which the agent's launch replaces with a +/// pipe, since every execution streams its input; its standard output and error are the core's +/// pipes. The plain-process core resets the child's priority and credentials, enters its working +/// directory, and executes the workload in its own process group. pub(crate) fn direct_request( config: &AgentConfig, execution: &GuestExecPayload, diff --git a/nvx-rs/src/guest/mappings.rs b/nvx-rs/src/guest/mappings.rs index fd5c5a979..4eb196dc9 100644 --- a/nvx-rs/src/guest/mappings.rs +++ b/nvx-rs/src/guest/mappings.rs @@ -7,9 +7,9 @@ //! [last section](#host-mapping-table) describes, and hands their mappings to the root of its //! caller's choice. //! -//! NVX mounts mappings through its mapper, which binds each beneath the descriptor of its root. -//! The mapper, like the rest of the guest's space, is nvx-init's internal reference code, private -//! to this crate. +//! The caller mounts the mappings through a mapper of its own. NVX's mapper, which binds each +//! beneath the descriptor of its root, is part of the guest's space, nvx-init's internal +//! reference code, private to this crate. //! //! # Host mapping table //! @@ -23,9 +23,8 @@ //! table, so that the agent starts no workload on a partial table. The caller keeps the mappings //! until its teardown. //! -//! NVX's managed agent applies its host mapping table through one, and through a mapper beneath -//! its own root, `/`, in direct mode, from its mount of the host's export, an aggregate, whose -//! root, which lists the aggregate's children, no table maps: a table refuses an empty source. +//! A table takes its entries from an aggregate export, whose root, which lists the aggregate's +//! children, no table maps: it refuses an empty source. mod kernel; pub(crate) mod mapper; diff --git a/nvx-rs/src/guest/mappings/table.rs b/nvx-rs/src/guest/mappings/table.rs index fc289bf96..e8fb76a09 100644 --- a/nvx-rs/src/guest/mappings/table.rs +++ b/nvx-rs/src/guest/mappings/table.rs @@ -40,9 +40,8 @@ use crate::protocol::{self, GuestHostMap, MAX_HOST_MAPPINGS, ProtocolError}; /// The table unmounts nothing: the caller keeps the mappings that it applied, and unmounts them /// itself, in reverse order, if it ever does. /// -/// NVX's managed agent applies its table through one, beneath its own root, `/`, in direct mode, -/// with a mapper that it opens at the first part that the table hands it, so that a table of zero -/// mappings opens nothing. +/// A caller can open its mapper at the first part that the table hands it, so that a table of +/// zero mappings opens nothing. /// /// ```no_run /// use nvx::guest::mappings::{Mapping, MappingTable, TableError}; diff --git a/nvx-rs/src/guest/mod.rs b/nvx-rs/src/guest/mod.rs index 3e788ca2e..ed45d3c64 100644 --- a/nvx-rs/src/guest/mod.rs +++ b/nvx-rs/src/guest/mod.rs @@ -1,31 +1,31 @@ -//! The programs that run in NVX's Linux guests, which ship as `/sbin/nvx-*`, and the libraries -//! on which any guest init or agent builds: [`nvxs`], the guest's I/O driver of the NVXS session; -//! [`terminal`], the terminal on which it serves that session; [`mappings`], the host mappings -//! that it mounts, and its account of a host's mapping table; and, on x86-64, [`snapshot`], which -//! composes a strictly checked Fresh capture by one caller deadline, -//! without execution control, with nothing mounted from the devices that a template's clones -//! replace, and which repairs each clone that a restore of the snapshot starts before it returns: -//! it activates the restore's memory target, reseeds the kernel's random number generator, and -//! finishes the restore, with its acknowledgement; and `activation`, through which each repaired -//! clone then takes its own configuration from the host, in one Activate on a restricted control -//! session. +//! The guest's side of the library, for Linux guests: the calls on which any guest init or agent +//! builds, in its own process. //! -//! The [guest-agent guidelines] give an init agent's contract for its space, its executions, a -//! capture, and a restore, with nvx-init as the worked example; the library enforces none of it. +//! - [`nvxs`]: the guest's I/O driver of the NVXS session, and [`terminal`]: the terminal on +//! which an agent serves that session, such as the VM's control console. +//! - [`mappings`]: host mappings, and the account of a host's mapping table, which hands each of +//! the table's parts to the caller's own mapper. +//! - [`reseed`]: a restore's fresh entropy, which starts with the VM's new [`GenerationId`], and +//! the reseed of the kernel's random number generator with it. +//! - On x86-64, `time`: the time ABI's cold-boot step and the start of its daemon, a process that +//! the caller supervises, as typed calls. +//! - On x86-64, [`snapshot`]: a strictly checked Fresh capture by one caller deadline, without +//! execution control, with nothing mounted from the devices that a snapshot's clones replace, +//! which repairs each clone that a restore of the snapshot starts before it returns: it +//! activates the restore's memory target, reseeds the kernel's random number generator, and +//! finishes the restore, with its acknowledgement. +//! - On x86-64, `activation`: the activation through which each repaired clone then takes its own +//! configuration from the host, in one Activate on a restricted control session. //! -//! [guest-agent guidelines]: https://github.com/microsoft/nvx/blob/dev/doc/guest-agent-guidelines.md +//! The library builds no space and runs no execution: the guest's space, the root that an init +//! assembles and what it mounts in it, and the executions that it runs are each init's own. The +//! [guest-agent guidelines] give an init agent's contract for its space, its executions, a +//! capture, and a restore, with nvx-init, NVX's PID 1 for layered roots, as the worked example; +//! the library enforces none of it. The contracts that every space reads stay public: the mapping +//! values and [`MappingTable`](mappings::MappingTable), and the tokens and records of +//! [`protocol`](crate::protocol) and [`activation`](crate::activation). //! -//! The guest's space, the root that an init assembles and what it mounts in it, is each init's -//! own. NVX's own space is nvx-init's internal reference code, private to the crate, which NVX's -//! guest programs use. It is `root`, which assembles a layered root, mounts live shares in it, -//! and tears it down; `view`, which sets the VM's hostname, -//! machine ID, and resolver file, whose binds the root owns; `gpt`, which reads the partition -//! table of a GPT disk that the kernel attached as a block device; the mapper of `mappings`, which -//! mounts host mappings beneath the descriptors of the roots that it names; and the activation's -//! application, with which a clone assembles its root and -//! applies its VM-wide view without starting a process. The contracts that every space reads -//! stay public: the mapping values and [`MappingTable`](mappings::MappingTable), and the tokens -//! and records of [`protocol`](crate::protocol) and [`activation`](crate::activation). +//! [guest-agent guidelines]: https://github.com/microsoft/nvx/blob/dev/doc/guest-agent-guidelines.md //! //! The guidelines quote this excerpt, in which an init applies one part of the host's mapping //! table through its own mapper, `mount`, and admits workloads only once the table is complete: @@ -43,67 +43,11 @@ //! } //! ``` //! -//! Each other module holds one program, and the crate's binary of the same name runs its `main`. -//! Two of them also offer their steps as typed calls, which any guest init or agent runs in its -//! own process and their programs run on: on x86-64, `time`'s `TimeAbi` runs the time ABI's -//! cold-boot step and starts its daemon, while [`snapshot`] runs its capture and restore finish, -//! and [`reseed`] reseeds the kernel's random number generator with a restore's fresh entropy, -//! which starts with the new [`GenerationId`]. The -//! program of the hidden `ingress` module, the announcer of the guest's listeners, offers no call: -//! activation reports the publication and gateway to its caller, which runs the program, or has -//! `nvx-ingress start` run it from a socket that it binds for it. The -//! libraries need none of NVX's control protocol, managed agent, or workload identity, and NVX's -//! managed agent opens its control terminal and applies its host mapping table through them. -//! On x86-64, the hidden `init` module holds -//! `nvx-init`, a PID 1 for layered roots, which takes PID 1 over from their early boot with the -//! guest's helpers, the time daemon and the announcer: it runs with them ahead of the workloads, -//! collects every child's status, and ends the VM after `SIGTERM` or once either helper ends. -//! -//! The programs keep the command lines, exit statuses, and output of the C programs that they -//! replace, except that numeric arguments are plain decimal, with a `-` sign where `nvx-time`'s -//! test hooks take negative numbers, or `0x`-prefixed hexadecimal where the C program also read -//! hexadecimal, floating-point arguments plain decimal with an optional exponent, error messages -//! describe `errno` values with `nix`'s text, and the programs read the kernel's text, such as -//! its socket tables, sysctls, rates, CPU lists, and timer list, their own records, and their -//! test fixtures only in the form in which they are written, rejecting the signed, prefixed, and -//! other lenient forms that C's `strtoul`, `strtod`, and `scanf` also read. Nor do they keep -//! what only the C programs' fixed buffers and string handling did to oversized or malformed -//! input, such as a length limit on a valid CPU list, an unknown option that a later valid one -//! masked, or a NUL byte that ended a diagnostic. -//! -//! # Conventions -//! -//! - Program and library modules deny unsafe code. Only the private `sys` module may use it, -//! behind documented safe operations. It makes every system call through `nix`, with the -//! enabled, default-free feature set, or through the standard library, which reads and writes -//! whole files and the MSR devices, except the few spawn, managed-agent, and time-tool -//! operations that neither provides, which `sys` documents, and the programs take random bytes -//! from the `getrandom` crate. `sys` also names the kernel's and the processor's interfaces -//! once: the procfs, sysfs, and device paths, OpenVMM's I/O ports, CPUID leaves and MSRs, and -//! the kernel's flags, so that the programs refer to them by name. -//! - Descriptors and mappings have owners that release them when dropped. Where a C program -//! reported a cleanup failure, a consuming `close` or `unmap` returns it instead. Raw -//! descriptor integers never leave `sys`, and no resource is dropped by hand: where a C -//! program released one before a later step, its scope ends there, for example in a helper -//! function that returns what the later step needs. State that a program changes for a while, -//! such as a lock, a signal mask, a thread's CPU affinity or scheduling policy, a mount, a -//! created file, or a kernel setting, likewise has an owner that restores it when dropped. -//! - A descriptor stays with the library that opened it: one that `sys` opens through `nix` is -//! read and written only through `sys`, and one that the standard library opens only through -//! it, except that a test may hand one to `FileDescriptor::from_owned`, which takes it over -//! whole. No descriptor thus has two owners or two buffers; a standard-library adapter such as -//! `BufReader` may add the one buffer of a `sys` descriptor through the `std::io::Read` that -//! `FileDescriptor` implements, and a caller that takes `std::io::Write`, such as [`nvxs`], -//! writes one through the `std::io::Write` that it implements, which buffers nothing. -//! `sys::Stdout` and `sys::DirectStdout` write standard output directly, past the standard -//! library's line buffer, so the programs never print with the standard library's macros: -//! `print!` would reorder its buffered output with theirs, and `eprint!` would bypass the error -//! streams that the programs' tests capture. Clippy denies both here. -//! - Every program fails with the shared `CommandError`, whose kind selects the program's exit -//! status from the shared `ExitStatus`. Every module, type, field, variant, constant, static, -//! and function is documented, which CI enforces with `clippy::missing_docs_in_private_items`. -//! - The programs use `nix`, `getrandom`, or the standard library rather than reimplement a -//! facility, and keep a parser of their own only for forms that no library parses. +//! NVX's own guest programs, which ship as `/sbin/nvx-*`, among them nvx-init and NVX's managed +//! agent, live in this module, private to the crate, with nvx-init's space, its internal +//! reference code: each binary calls the hidden `main` of its program's module. The calls above +//! need none of NVX's control protocol, its managed agent, or its workload identity, and NVX's +//! programs run on them too, such as `nvx-time` on `time` and `nvx-reseed` on [`reseed`]. #![deny(unsafe_code)] #![deny(clippy::print_stdout, clippy::print_stderr)] diff --git a/nvx-rs/src/guest/root.rs b/nvx-rs/src/guest/root.rs index 53b7ae092..7176a4700 100644 --- a/nvx-rs/src/guest/root.rs +++ b/nvx-rs/src/guest/root.rs @@ -928,8 +928,8 @@ impl Root { /// stay until the VM powers off. A move that fails leaves the root where it was, unswitched. /// /// Paths resolve in the root from then on, but descriptors opened before the switch stay - /// usable: a [`Mapper`] still maps from a [`SourceRoot`](super::mappings::SourceRoot) beneath - /// a [`TargetRoot`] opened before it. + /// usable: a [`Mapper`] still maps from a [`SourceRoot`] beneath a [`TargetRoot`] opened + /// before it. pub(crate) fn switch(&mut self) -> Result<(), SwitchError> { if self.switched { return Err(SwitchError::Switched); @@ -1452,9 +1452,9 @@ pub(crate) fn bind_file_retained( } /// Binds the file or directory at `path`, which must be of `kind`, read-only at `target` beneath -/// `root`, with `kernel`, through the walk and attach of a runtime bind, -/// [`bind_retained_with`](mappings::mapper::bind_retained_with), from a source root at `path`'s -/// parent directory, and returns the bind, retaining a failed bind that its caller must detach. +/// `root`, with `kernel`, through the walk and attach of a runtime bind, [`bind_retained_with`], +/// from a source root at `path`'s parent directory, and returns the bind, retaining a failed bind +/// that its caller must detach. pub(crate) fn bind_runtime_file_retained( kernel: &mut dyn mappings::Kernel, root: &TargetRoot, diff --git a/nvx-rs/src/guest/status.rs b/nvx-rs/src/guest/status.rs index db28aa3bc..ff9c5af47 100644 --- a/nvx-rs/src/guest/status.rs +++ b/nvx-rs/src/guest/status.rs @@ -1,4 +1,52 @@ //! Exit statuses and failures shared by the guest programs, and the entry point of their `main`. +//! +//! The programs keep the command lines, exit statuses, and output of the C programs that they +//! replace, except that numeric arguments are plain decimal, with a `-` sign where `nvx-time`'s +//! test hooks take negative numbers, or `0x`-prefixed hexadecimal where the C program also read +//! hexadecimal, floating-point arguments plain decimal with an optional exponent, error messages +//! describe `errno` values with `nix`'s text, and the programs read the kernel's text, such as +//! its socket tables, sysctls, rates, CPU lists, and timer list, their own records, and their +//! test fixtures only in the form in which they are written, rejecting the signed, prefixed, and +//! other lenient forms that C's `strtoul`, `strtod`, and `scanf` also read. Nor do they keep +//! what only the C programs' fixed buffers and string handling did to oversized or malformed +//! input, such as a length limit on a valid CPU list, an unknown option that a later valid one +//! masked, or a NUL byte that ended a diagnostic. +//! +//! # Conventions +//! +//! The programs and the libraries of the guest module keep these conventions: +//! +//! - Program and library modules deny unsafe code. Only the private `sys` module may use it, +//! behind documented safe operations. It makes every system call through `nix`, with the +//! enabled, default-free feature set, or through the standard library, which reads and writes +//! whole files and the MSR devices, except the few spawn, managed-agent, and time-tool +//! operations that neither provides, which `sys` documents, and the programs take random bytes +//! from the `getrandom` crate. `sys` also names the kernel's and the processor's interfaces +//! once: the procfs, sysfs, and device paths, OpenVMM's I/O ports, CPUID leaves and MSRs, and +//! the kernel's flags, so that the programs refer to them by name. +//! - Descriptors and mappings have owners that release them when dropped. Where a C program +//! reported a cleanup failure, a consuming `close` or `unmap` returns it instead. Raw +//! descriptor integers never leave `sys`, and no resource is dropped by hand: where a C +//! program released one before a later step, its scope ends there, for example in a helper +//! function that returns what the later step needs. State that a program changes for a while, +//! such as a lock, a signal mask, a thread's CPU affinity or scheduling policy, a mount, a +//! created file, or a kernel setting, likewise has an owner that restores it when dropped. +//! - A descriptor stays with the library that opened it: one that `sys` opens through `nix` is +//! read and written only through `sys`, and one that the standard library opens only through +//! it, except that a test may hand one to `FileDescriptor::from_owned`, which takes it over +//! whole. No descriptor thus has two owners or two buffers; a standard-library adapter such as +//! `BufReader` may add the one buffer of a `sys` descriptor through the `std::io::Read` that +//! `FileDescriptor` implements, and a caller that takes `std::io::Write`, such as +//! [`nvxs`](super::nvxs), writes one through the `std::io::Write` that it implements, which +//! buffers nothing. `sys::Stdout` and `sys::DirectStdout` write standard output directly, past +//! the standard library's line buffer, so the programs never print with the standard library's +//! macros: `print!` would reorder its buffered output with theirs, and `eprint!` would bypass +//! the error streams that the programs' tests capture. Clippy denies both in the guest module. +//! - Every program fails with the shared [`CommandError`], whose kind selects the program's exit +//! status from the shared [`ExitStatus`]. Every module, type, field, variant, constant, static, +//! and function is documented, which CI enforces with `clippy::missing_docs_in_private_items`. +//! - The programs use `nix`, `getrandom`, or the standard library rather than reimplement a +//! facility, and keep a parser of their own only for forms that no library parses. use std::ffi::CString; use std::process::ExitCode; diff --git a/nvx-rs/src/guest/time/mod.rs b/nvx-rs/src/guest/time/mod.rs index c481561e4..e1a411f7a 100644 --- a/nvx-rs/src/guest/time/mod.rs +++ b/nvx-rs/src/guest/time/mod.rs @@ -1,5 +1,6 @@ -//! `nvx-time`, the guest side of NVX time ABI v2, and the typed calls through which any guest -//! init or agent runs the ABI's boot steps in its own process. +//! `nvx-time`, the guest side of NVX's time ABI, whose version is +//! [`TIME_ABI_VERSION`](crate::contract::TIME_ABI_VERSION), and the typed calls through which any +//! guest init or agent runs the ABI's boot steps in its own process. //! //! [`TimeAbi`] runs the cold-boot time step and, separately, starts the time daemon, a process //! that the caller supervises; it reads the VM generation ID, with typed results and errors. diff --git a/nvx-rs/src/host/config/mod.rs b/nvx-rs/src/host/config/mod.rs index e81c48e8c..b15aa5594 100644 --- a/nvx-rs/src/host/config/mod.rs +++ b/nvx-rs/src/host/config/mod.rs @@ -225,7 +225,7 @@ pub struct Capacity { pub capacity: u64, } -/// The length and block sizes of a sandbox block device. +/// The length and block sizes of a VM's block device, such as its image or its scratch. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct BlockGeometry { /// The length that the device presents, in bytes: nonzero, and a multiple of the logical diff --git a/nvx-rs/src/host/filesystem/verify.rs b/nvx-rs/src/host/filesystem/verify.rs index 1fb4a9e14..935f0ea93 100644 --- a/nvx-rs/src/host/filesystem/verify.rs +++ b/nvx-rs/src/host/filesystem/verify.rs @@ -26,10 +26,11 @@ use crate::host::platform; use crate::protocol::{HostMap, MAPS_TOKEN, MAX_HOST_MAPPINGS}; impl ExportPlan { - /// Checks that a plan read from a record is one that the CLI's planner could make for a root - /// that reserves `reserved`: at least one and at most [`MAX_CHILDREN`] children, none inside - /// another, each an absolute host path below a volume's root, with its host paths in the form - /// in which `plan` resolves them, whose policy paths lie inside it within OpenVMM's limits and + /// Checks that a plan, such as one read from a record, is one that a planner could make for a + /// root that reserves `reserved`: at least one and at most [`MAX_CHILDREN`] children, none + /// inside another, each an absolute host path below a volume's root, with its host paths + /// resolved, without a NUL character or a `..` component, and, on Windows, on a local drive, + /// without the verbatim prefix, whose policy paths lie inside it within OpenVMM's limits and /// keep its rules, and writable exactly when a bind maps a path of it read-write; and at least /// one and at most [`MAX_HOST_MAPPINGS`] binds, each of a known child, from a relative source /// that holds the host's names joined with `/`, that a guest path can hold, and that the child diff --git a/nvx-rs/src/host/mod.rs b/nvx-rs/src/host/mod.rs index 296aa3b61..f0256d3b5 100644 --- a/nvx-rs/src/host/mod.rs +++ b/nvx-rs/src/host/mod.rs @@ -1,68 +1,61 @@ -//! The host side of NVX. +//! The host's side of the library: a VM's runtime and configuration, its launch in a private +//! directory that its caller owns, for a cold boot, a capture, or a restore, its stop and its +//! reattachment, its snapshots and their restores, and verified connections to it. //! -//! - The private `artifacts` locates, for the CLI, the OpenVMM executable and the guest kernel and -//! initramfs, and every artifact of the guests that `nvx.py` runs, in a repository checkout or -//! a release. -//! - [`runtime`] holds what every launch shares: the paths of those three files and their -//! [`RuntimeIdentity`](runtime::RuntimeIdentity), the hypervisor, which -//! [`probe_hypervisor`](runtime::probe_hypervisor) checks, and the +//! - [`runtime`] holds what every launch shares: the paths of the OpenVMM executable and the +//! guest kernel and initramfs, and their [`RuntimeIdentity`](runtime::RuntimeIdentity), the +//! hypervisor, which [`probe_hypervisor`](runtime::probe_hypervisor) checks, and the //! [`LaunchOptions`](runtime::LaunchOptions). //! - [`config`] holds the configuration of a VM as one value, a [`VmConfig`](config::VmConfig), //! which it checks without reading the host: the VM's processors and memory, its root, the view //! that its workload has of it, its network, its export, its boot console, and what only its //! cold boot takes, with what a capture of it adds, a [`CaptureConfig`](config::CaptureConfig), //! whose identity names the capture's snapshots. -//! - [`network`] lowers egress rules into OpenVMM's rules, as its private `policy` (feature `cli`) -//! lowers the egress policy files that `nvx-rs run` and `nvx-rs sandbox` accept, and -//! [`filesystem`] checks and lowers the export of host paths through OpenVMM's aggregate -//! virtio-fs device, which its private planner (feature `cli`) plans for the CLI. -//! - [`report`] reads OpenVMM's outcome report. -//! - [`snapshot`] holds the compatibility key, [`SnapshotKey`](snapshot::SnapshotKey), derived -//! from a capture's context and [`SnapshotManifest`](snapshot::SnapshotManifest), -//! the strict manifest reader, and the capture's public time and eligibility information. -//! - [`transport`] verifies a VM's endpoint and runs the host's role of the NVXS session for it, -//! for any application protocol, over a platform transport. The private `session` (feature -//! `cli`) speaks NVX's own control protocol, `NVXC`, over it for NVX's programs. [`nvxs`] -//! retains compatibility paths. The private `platform` module holds the primitives of Linux and -//! Windows hosts. Other hosts compile but report NVX as unavailable. -//! - [`process`] names processes by verified references: their ID, start time, and host boot, -//! through which a pidfd or a process handle holds them, so that waits sleep until a process -//! exits and termination reaches only it, whether or not the caller started it. -//! - The private `sandbox` is the launch profile of `nvx-rs sandbox`, for the CLI (feature -//! `cli`): a container over read-only EROFS layers and a private ext4 scratch disk, with live -//! shares, and the checks of its options. Its `distro` binds a distro layer to what explicit -//! verification found, so that the CLI's launches check the layer through the private `seal`, -//! which seals open files from their metadata alone, and its `scratch` binds and claims a -//! scratch image. Outside the CLI, [`vm`] and [`snapshot`] read the block geometry of an image -//! through it, and a snapshot's key names the tag of its shares' virtio-fs device. //! - [`vm`] holds a caller-owned VM directory, private to the current user, the fixed names of //! its files and endpoints, and a checked cold, capture or restore [`Prepared`](vm::Prepared) //! launch with its opened log and control credential, which the library never writes. A //! restore from a [`Snapshot`](snapshot::Snapshot) hard-links the snapshot's OpenVMM files into -//! the VM directory and binds its restore-ready listener before any spawn. +//! the VM directory and binds its restore-ready listener before any spawn. [`vm::spawn`] +//! starts OpenVMM, detached, [`vm::stop`] ends a VM, and [`vm::verify`] and [`vm::reattach`] +//! find one again from the [`SavedVm`](vm::SavedVm) that its caller saved. +//! - [`snapshot`] holds the strict snapshot directory, [`Snapshot`](snapshot::Snapshot), the +//! finish of a capture, [`finish_capture`](snapshot::finish_capture), the compatibility key, +//! [`SnapshotKey`](snapshot::SnapshotKey), derived from a capture's context and +//! [`SnapshotManifest`](snapshot::SnapshotManifest), the strict manifest reader, and the +//! capture's public time and eligibility information. //! - [`restore`] orders caller-driven activation to Ready without I/O or a clock, and listens for -//! OpenVMM's restore-ready event from the held VM process. The private `restore_driver` (feature -//! `cli`) drives it over the private `session` for NVX's blocking callers, through Ready, the -//! mapping table and the workload's start. -//! - `cli` (feature `cli`) is the `nvx-rs` command-line interface, which runs microVMs and -//! sandboxes, looks up and downloads artifacts, and compiles egress policies. +//! OpenVMM's restore-ready event from the held VM process. +//! - [`transport`] verifies a VM's endpoint and runs the host's role of the NVXS session for it, +//! for any application protocol, over a platform transport. [`nvxs`] retains compatibility +//! paths to it. +//! - [`process`] names processes by verified references: their ID, start time, and host boot, +//! through which a pidfd or a process handle holds them, so that waits sleep until a process +//! exits and termination reaches only it, whether or not the caller started it. +//! - [`report`] reads OpenVMM's outcome report. +//! - [`network`] lowers a network posture and its egress rules into OpenVMM's options, and +//! [`filesystem`] checks and lowers the export of host paths through OpenVMM's aggregate +//! virtio-fs device. //! -//! The private `paths` module makes host paths absolute and resolves their links, with -//! `std::path` and `std::fs`, for the sandbox launch profile and the CLI. The private `spawn` -//! module starts a VMM for [`vm`], detached, passing it only the descriptors or handles that the -//! caller allows, and no other descriptor or handle of the caller's. +//! The library keeps no record of VMs, launches, captures, or snapshots, and takes no lock that +//! its caller sees. It starts OpenVMM, in [`vm::spawn`], and no other process, and supervises no +//! VM: it ends a process on its own only when its spawn fails. The caller saves what it needs to +//! find its VMs again, orders its own operations on each VM and each snapshot, and stops each VM +//! that it launched, with [`vm::stop`], or keeps its reference to stop it later. Linux and +//! Windows hosts run VMs; other hosts compile the module but report NVX as unavailable. //! //! The children of `host` depend on one another without cycles: each builds only on children -//! beneath it, as [`vm`] does on `spawn`, and `spawn` on `platform`. An item that -//! a lower child needs from a higher one moves down, and the higher child re-exports it where it -//! was. `tests/host_modules.rs` reads the crate's sources and fails on a cycle, which it lists -//! with a reference for each of its dependencies. +//! beneath it, as [`vm`] does on the private `spawn`, which starts a VMM, detached, passing it +//! only the descriptors or handles that the caller allows, and `spawn` on the private +//! `platform`, which holds the primitives of Linux and Windows hosts. An item that a lower child +//! needs from a higher one moves down, and the higher child re-exports it where it was. +//! `tests/host_modules.rs` reads the crate's sources and fails on a cycle, which it lists with a +//! reference for each of its dependencies. //! //! The host modules deny unsafe code. On Unix, the private `sys` module confines the system calls //! that the standard library lacks behind documented safe functions, which call `nix`. The -//! Windows platform module and the Windows module of `seal` still call `windows-sys` directly, -//! and the CLI's identification of the host CPU executes `CPUID` through its `core::arch` -//! intrinsic, which the crate's minimum Rust version still marks unsafe. +//! Windows platform module and the Windows module of the private `seal` still call `windows-sys` +//! directly, and the CLI's identification of the host CPU executes `CPUID` through its +//! `core::arch` intrinsic, which the crate's minimum Rust version still marks unsafe. #![deny(unsafe_code)] pub(crate) mod artifacts; diff --git a/nvx-rs/src/host/network/mod.rs b/nvx-rs/src/host/network/mod.rs index a2e61a623..969b90527 100644 --- a/nvx-rs/src/host/network/mod.rs +++ b/nvx-rs/src/host/network/mod.rs @@ -1,16 +1,15 @@ -//! Translation of a network posture into OpenVMM options, and, in the private `policy` (feature -//! `cli`), the egress policy files of the CLI. +//! Translation of a network posture into OpenVMM options. //! //! OpenVMM's portable profile enforces an egress default plus IPv4 and IPv6 allow and deny rules //! that match a network of one family and, optionally, one protocol (TCP, UDP, or ICMP) and, for //! TCP or UDP, one port or an inclusive range of ports; deny rules take precedence. -//! [`EgressRule`]s are lowered into that form exactly, by the lowering that also compiles the CLI's -//! egress policy files: exceptions are subtracted from their networks, a rule without -//! destinations matches `0.0.0.0/0` and `::/0`, `any` protocol with a port or a port range becomes -//! a TCP and a UDP rule, destinations merge into the fewest networks that cover them, rules for -//! every port of a protocol subsume narrower rules, and overlapping and adjacent port ranges -//! merge. Each action emits at most [`MAX_RULES`] OpenVMM rules, sorted by family, address, prefix -//! length, protocol, and ports, which [`NetworkPolicy::native_egress_rules`] returns. +//! [`EgressRule`]s are lowered into that form exactly: exceptions are subtracted from their +//! networks, a rule without destinations matches `0.0.0.0/0` and `::/0`, `any` protocol with a +//! port or a port range becomes a TCP and a UDP rule, destinations merge into the fewest networks +//! that cover them, rules for every port of a protocol subsume narrower rules, and overlapping +//! and adjacent port ranges merge. Each action emits at most [`MAX_RULES`] OpenVMM rules, sorted +//! by family, address, prefix length, protocol, and ports, which +//! [`NetworkPolicy::native_egress_rules`] returns. //! //! Host-loopback access is bidirectional: with it allowed, the guest reaches host loopback //! services through its gateway, and OpenVMM publishes the guest's listeners on host loopback. diff --git a/nvx-rs/src/host/platform/windows/log_claim.rs b/nvx-rs/src/host/platform/windows/log_claim.rs index 1f9fa708e..2d0646987 100644 --- a/nvx-rs/src/host/platform/windows/log_claim.rs +++ b/nvx-rs/src/host/platform/windows/log_claim.rs @@ -38,10 +38,10 @@ use super::{last_error, owned, raw, wide}; /// Claims a launch's fresh log, which `file` holds open. /// /// On Windows the claim needs no further step: it is the job object of the log, in which -/// [`spawn_detached`] starts the VMM, or else the log's handles that the VMM inherits, as +/// [`spawn_with_stdio`] starts the VMM, or else the log's handles that the VMM inherits, as /// [`log_claim_held`] describes. /// -/// [`spawn_detached`]: crate::host::platform::windows::spawn_detached +/// [`spawn_with_stdio`]: crate::host::platform::windows::spawn_with_stdio #[cfg(feature = "cli")] pub(crate) fn claim_log(_file: &fs::File) -> io::Result<()> { Ok(()) @@ -73,7 +73,7 @@ pub(super) fn log_job_id(log: &fs::File) -> io::Result> { } } -/// Creates the job object of the log that `log` holds open, in which [`spawn_detached`] starts +/// Creates the job object of the log that `log` holds open, in which [`spawn_with_stdio`] starts /// the VMM that writes the log, so that the processes of the job are the log's claim, as /// [`log_claim_held`] describes. The job limits no resource, but it lets no process that the VMM /// starts break away from it: one that asks to, with `CREATE_BREAKAWAY_FROM_JOB`, fails with @@ -91,7 +91,7 @@ pub(super) fn log_job_id(log: &fs::File) -> io::Result> { /// of the same file ID, or of the same file before it was truncated, that a process still holds /// would: the VMM would share it. /// -/// [`spawn_detached`]: crate::host::platform::windows::spawn_detached +/// [`spawn_with_stdio`]: crate::host::platform::windows::spawn_with_stdio pub(crate) fn create_log_job(log: &fs::File) -> io::Result> { let Some(id) = log_job_id(log)? else { return Ok(None); @@ -327,15 +327,15 @@ pub(super) fn log_job_processes(id: (u64, u128)) -> io::Result> { /// the file `identity`. /// /// While the job object of the log can be opened by its name, the claim is its processes. -/// [`spawn_detached`] starts the VMM in the job, every process that the VMM starts joins it, and -/// no other process does. The job keeps its name while a process holds a handle to it, not merely -/// while it holds processes, and the spawn closes its own: a process that another thread created -/// while the VMM's spawn had the log's handles inheritable holds the log, and the job's handle with -/// it, so the job can be opened by its name for as long as that process holds the log, and the -/// process, which never joins the job, does not hold the claim. What takes the name but is not the -/// user's job, as [`log_job_named`] tells it, is not the log's job, and leaves the claim to the -/// log's handles: another user cannot end a launch whose VMM still runs by creating an empty job -/// under that name, or a symbolic link under it to an empty job of the user. +/// [`spawn_with_stdio`] starts the VMM in the job, every process that the VMM starts joins it, +/// and no other process does. The job keeps its name while a process holds a handle to it, not +/// merely while it holds processes, and the spawn closes its own: a process that another thread +/// created while the VMM's spawn had the log's handles inheritable holds the log, and the job's +/// handle with it, so the job can be opened by its name for as long as that process holds the +/// log, and the process, which never joins the job, does not hold the claim. What takes the name +/// but is not the user's job, as [`log_job_named`] tells it, is not the log's job, and leaves the +/// claim to the log's handles: another user cannot end a launch whose VMM still runs by creating +/// an empty job under that name, or a symbolic link under it to an empty job of the user. /// /// Without the user's job, as for a VMM that no such process outlives, that a launch of an earlier /// version started outside one, or whose job's name something else took, and for a log that is @@ -346,7 +346,7 @@ pub(super) fn log_job_processes(id: (u64, u128)) -> io::Result> { /// inherited the log, holds it. The identity is checked again on the exclusive handle, because a /// rename between the opens could have replaced the file. /// -/// [`spawn_detached`]: crate::host::platform::windows::spawn_detached +/// [`spawn_with_stdio`]: crate::host::platform::windows::spawn_with_stdio #[cfg(feature = "cli")] pub(crate) fn log_claim_held(path: &Path, identity: (u64, u64)) -> io::Result { let replaced = || io::Error::other(format!("{} is not the claimed log", path.display())); diff --git a/nvx-rs/src/host/runtime/mod.rs b/nvx-rs/src/host/runtime/mod.rs index 5485c69d6..e18131794 100644 --- a/nvx-rs/src/host/runtime/mod.rs +++ b/nvx-rs/src/host/runtime/mod.rs @@ -84,7 +84,7 @@ pub const MAX_IDENTITY_LEN: usize = 128; const SHA256_HEX_LEN: usize = 64; /// Opaque text of 1 to [`MAX_IDENTITY_LEN`] printable ASCII characters and no space: the form of -/// an [`ArtifactIdentity`] and of a control contract revision, which the store only compares. +/// an [`ArtifactIdentity`] and of a control contract revision, which the library only compares. #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Token(String); @@ -116,7 +116,7 @@ impl Token { /// a release's `SHA256SUMS` lists for the file. /// /// It is an opaque token of 1 to [`MAX_IDENTITY_LEN`] printable ASCII characters without spaces, -/// which the store only compares. Nothing here reads or hashes an artifact: a digest computed at +/// which the library only compares. Nothing here reads or hashes an artifact: a digest computed at /// launch would cost time in proportion to the artifact's size on every start. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] diff --git a/nvx-rs/src/host/snapshot/key.rs b/nvx-rs/src/host/snapshot/key.rs index e69402ed0..4277a7116 100644 --- a/nvx-rs/src/host/snapshot/key.rs +++ b/nvx-rs/src/host/snapshot/key.rs @@ -272,7 +272,7 @@ fn guest_network_of(nic: &Nic) -> String { /// /// A key holds, and nothing else: /// -/// - **Bundle and contracts:** the identities of OpenVMM, the guest kernel, and the guest +/// - **Runtime and contracts:** the identities of OpenVMM, the guest kernel, and the guest /// initramfs; and the versions /// of the key's encoding and of the boot, snapshot, device-state, time, restore-packet, /// control, and activation contracts. @@ -302,8 +302,8 @@ fn guest_network_of(nic: &Nic) -> String { /// /// The key comes from metadata alone: the artifacts' identities that their install metadata /// records, the capture's inputs, and its manifest. It never reads or digests a binary, -/// an image, the RAM, or the saved state. A bundle that changes any of its artifacts therefore -/// makes templates of a new key: the templates of the old one are not recertified. +/// an image, the RAM, or the saved state. A runtime that changes any of its artifacts therefore +/// makes templates of a new key, and restores none of the old key's: they are captured again. /// /// Its [`identity`](Self::identity) is the SHA-256 digest of its /// canonical compatibility prefix, after a domain that no other digest of this crate shares. @@ -608,7 +608,7 @@ impl SnapshotKey { /// Version 1 holds, in order, each value encoded as the [module](super) describes: /// /// 1. [`VERSION`](Self::VERSION), a `u16`. - /// 2. The bundle and contracts: the OpenVMM, kernel, and initramfs identities, texts; the boot + /// 2. The runtime and contracts: the OpenVMM, kernel, and initramfs identities, texts; the boot /// contract version, a `u64`; the control contract revision, a text; the control session /// version, a `u64`; and the snapshot format, device-state schema, /// time ABI, restore packet, and activation contract versions, `u64`s. diff --git a/nvx-rs/src/host/vm.rs b/nvx-rs/src/host/vm.rs index dfc801f0e..de8d9af3f 100644 --- a/nvx-rs/src/host/vm.rs +++ b/nvx-rs/src/host/vm.rs @@ -276,14 +276,14 @@ impl std::os::windows::io::AsHandle for InheritedHandle { /// spawning, and uses the opened log for stdout and stderr. It allows only the standard streams /// and `inherit`, under their original descriptor or handle numbers, into the child. /// -/// Follow [`spawn`]'s launch contract: detach on Linux, clear only the child's owner umask bits, -/// and keep allowed descriptors close-on-exec in the caller; on Windows, use an explicit handle -/// list, restore temporary inheritability, start without a console in the log-named job, -/// refuse a taken or untrusted job name, and honor `breakaway_from_job`. Windows callers -/// coordinate their other spawns with the inheritance window. If setup fails after spawning, -/// terminate and reap only that child before returning an error. The caller journals recovery -/// data and keeps the VM directory and runtime inputs stable; this value starts no process. -/// A restore retains its already-bound ready listener in `ready`, outside the child +/// Follow [`spawn`](fn@spawn)'s launch contract: detach on Linux, clear only the child's owner +/// umask bits, and keep allowed descriptors close-on-exec in the caller; on Windows, use an +/// explicit handle list, restore temporary inheritability, start without a console in the +/// log-named job, refuse a taken or untrusted job name, and honor `breakaway_from_job`. Windows +/// callers coordinate their other spawns with the inheritance window. If setup fails after +/// spawning, terminate and reap only that child before returning an error. The caller journals +/// recovery data and keeps the VM directory and runtime inputs stable; this value starts no +/// process. A restore retains its already-bound ready listener in `ready`, outside the child /// inheritance allowlist. #[derive(Debug)] pub struct VmmCommand { diff --git a/nvx-rs/src/hypervisor.rs b/nvx-rs/src/hypervisor.rs index 9cb73bc08..ac39ee1a6 100644 --- a/nvx-rs/src/hypervisor.rs +++ b/nvx-rs/src/hypervisor.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use crate::error::{Error, Result}; -/// Hypervisor that runs the sandbox VMs. +/// Hypervisor that runs the VMs. /// /// With the `serde` feature, it serializes as its OpenVMM spelling: `kvm`, `mshv`, or `whp`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/nvx-rs/src/lib.rs b/nvx-rs/src/lib.rs index 645c407ac..6d0c73b0e 100644 --- a/nvx-rs/src/lib.rs +++ b/nvx-rs/src/lib.rs @@ -1,22 +1,31 @@ -//! NVX mechanics: the OpenVMM control protocol, and the launch, policy lowering, and supervision -//! of NVX microVMs. +//! Mechanisms and value types with which a program runs NVX microVMs on OpenVMM: a VM's +//! configuration and its launch, in a directory that the program owns, for a cold boot, a +//! capture, or a restore; one snapshot type, with a strict directory format; a restore that the +//! program drives with its own I/O; verified connections to a running VM, and reattachment to +//! one; the contracts that a guest's space reads; and the guest's side of a capture and of a +//! restore's activation. //! -//! An NVX sandbox is a microVM that OpenVMM runs with the managed lifecycle: the guest's init -//! starts a managed agent, which runs workloads that the host sends it over an authenticated -//! control console. This crate holds everything it takes to drive one: +//! The library keeps no durable state, runs no workload, and builds no guest space. On the host, +//! it starts OpenVMM, and no other process, and supervises no VM: the program stops each VM that +//! it launched, or keeps its reference to stop it later. Whatever a deployment needs beyond the +//! mechanisms, the program owns: the runtime's files and the images that it launches, the +//! records with which it finds its VMs and snapshots again, its locks, its protocol with its +//! guest, its deadlines, and its recovery, as the [library guide] lists under +//! [What the caller owns], beside the cold-boot, restore, and reattach workflows. In the guest, +//! each init owns its space and its executions, as the [guest-agent guidelines] describe. //! -//! - [`protocol`]: the codec of the control console and the `NVXS` session that OpenVMM brokers, -//! which carries any application protocol, the guest targets that a sandbox's live shares may -//! not take, and the exit statuses with which a clone's guest fails fast. NVX's own control -//! protocol, `NVXC`, is private to the crate. It has no dependencies, so the host, the guest, -//! and test doubles share it. +//! - [`protocol`]: the `NVXS` session that OpenVMM brokers between a host and its guest, which +//! carries any application protocol; the host's mapping table on the wire; the kernel +//! command-line tokens of a guest's root, view, and export; the guest targets that live shares +//! may not take; and the exit statuses with which a clone's guest fails fast. It has no +//! dependencies, so the host and the guest share it. //! - [`activation`]: the activation record of a layered root, the bounded, versioned, non-secret -//! record of the settings that its activation applies, which a clone restored from a template +//! record of the settings that its activation applies, which a clone restored from a snapshot //! reads in place of its captured configuration, and what Ready reports about the activation. //! It needs no features, so the host, the guest, and every adapter's framing share it. -//! - [`contract`]: the control-contract versions that this crate speaks and the compatibility of -//! NVX releases with them, and the versions of the contracts through which OpenVMM restores a -//! snapshot. +//! - [`contract`]: the versions of the contracts that this crate speaks with OpenVMM and the +//! guest, and the compatibility of NVX releases with them, with the versions of the contracts +//! through which OpenVMM restores a snapshot. //! - [`net`]: IP networks in CIDR notation and their subtraction, and the forms of a VM's //! hostname and guest network address. //! - [`gpt`]: GUID Partition Table disks whose first partition configures an adapter and whose @@ -26,23 +35,28 @@ //! - [`uuid`]: UUIDs, such as the GUIDs of a GPT disk, in their canonical text, the one form that //! NVX writes and reads, and in the layout in which GPT stores them. It has no dependencies, so //! the host and the guest share it. -//! - [`Hypervisor`]: the hypervisors that run the VMs. -//! - [`host`] (feature `host`): the OpenVMM command line, the lowering of network and filesystem -//! policies, the host's NVXS connection, process and platform primitives, VMs that a caller -//! creates, starts, reattaches to, and stops in private directories that it owns, and the -//! manifests and compatibility keys of templates. -//! - `guest` (feature `guest`, Linux): the programs that run in NVX guests, such as -//! `nvx-console-pending`, the guest's NVXS connection, the libraries through which any guest -//! init or agent opens the terminal on which it serves its session (`guest::terminal`), and -//! describes host mappings and keeps the account of a host's mapping table -//! (`guest::mappings`), the typed calls through -//! which it runs the time ABI's steps (`guest::time`) and reseeds after a restore -//! (`guest::reseed`) in its own process, -//! and the capture of a template's guest, which repairs its clones (`guest::snapshot`), and -//! their activation (`guest::activation`). The crate's `nvx-*` guest binaries run the programs. -//! NVX's own guest space, which assembles a layered root, reads GPT disks, mounts host mappings, -//! and applies a clone's activation, is nvx-init's internal reference code, private to the -//! crate. +//! - [`Hypervisor`]: the hypervisors that run the VMs, and [`Error`]: the crate's error, whose +//! [`ErrorKind`]s callers act on. +//! - [`host`] (feature `host`): a VM's runtime and configuration; its launch, stop, and +//! reattachment in a private directory that its caller owns; snapshots, their capture's finish, +//! and the restores that their caller drives; the lowering of network and filesystem policies; +//! and verified connections to a VM's endpoint, verified process references, and OpenVMM's +//! outcome reports. +//! - `guest` (feature `guest`, Linux): the guest's side, for any guest init or agent: its NVXS +//! connection and the terminal on which it serves it (`guest::nvxs`, `guest::terminal`), the +//! account of a host's mapping table (`guest::mappings`), the time ABI's steps in its own +//! process (`guest::time`), a capture that repairs each restore of its snapshot +//! (`guest::snapshot`), a restore's activation (`guest::activation`), and its reseed +//! (`guest::reseed`). +//! +//! NVX's own programs, its `nvx-rs` command-line interface, its guest binaries, and the fake +//! OpenVMM of its tests, build on the library and live in this crate, private to it, with the +//! code that only they use, such as NVX's own control protocol. Each binary calls an entry point +//! that `#[doc(hidden)]` hides, which is no part of the API. +//! +//! [library guide]: https://github.com/microsoft/nvx/blob/dev/doc/library.md +//! [What the caller owns]: https://github.com/microsoft/nvx/blob/dev/doc/library.md#what-the-caller-owns +//! [guest-agent guidelines]: https://github.com/microsoft/nvx/blob/dev/doc/guest-agent-guidelines.md //! //! # Example //! diff --git a/nvx-rs/src/protocol/cmdline.rs b/nvx-rs/src/protocol/cmdline.rs index 79bf0affc..2baab1223 100644 --- a/nvx-rs/src/protocol/cmdline.rs +++ b/nvx-rs/src/protocol/cmdline.rs @@ -26,9 +26,9 @@ //! [`HostExport::parse`](super::export::HostExport::parse), //! [`RootLayout::parse`](super::root::RootLayout::parse), and //! [`parse_host_mapping_count`](super::parse_host_mapping_count) read their tokens with it, and -//! so do the time ABI's check of report-only mode, in `nvx::guest::time`, and the CLI's check of -//! the user's kernel parameters of a sandbox, which splits them both as the kernel and as the -//! init does, and compares their names as the kernel does. +//! so do the time ABI's check of report-only mode, in `nvx::guest::time`, and the check of a +//! boot's own kernel parameters, in `nvx::host::config::VmConfig::check`, which splits them +//! both as the kernel and as the init does, and compares their names as the kernel does. //! //! The kernel leaves OpenVMM's `virtnet_dns=` and `virtnet_gw=` tokens to the guest, which reads //! them from the whole command line, as OpenVMM checks them: diff --git a/nvx-rs/src/protocol/maps.rs b/nvx-rs/src/protocol/maps.rs index 4997087d7..8696c8ba1 100644 --- a/nvx-rs/src/protocol/maps.rs +++ b/nvx-rs/src/protocol/maps.rs @@ -1,12 +1,14 @@ -//! `MAPS` payloads: the host mapping table that the `APP_MAPS` requests of NVX's own control -//! protocol, `NVXC`, hand the guest agent. +//! `MAPS` payloads: the host mapping table on the wire, which a host sends its guest agent in +//! parts, in its own application protocol, as NVX's own control protocol does in its `APP_MAPS` +//! requests. //! //! A payload holds the index of its first entry (`u32`) and its number of entries (`u32`), //! followed by the entries. Each entry holds its flags (`u16`), the lengths of its source and //! target (`u16` each), and then both. A source names a path relative to the guest's mount of the //! host's export, and an empty source names the export's root itself, which is how the table of a -//! directory export maps the whole directory. NVX's managed agent, whose export is an aggregate, -//! whose root lists its children, refuses an empty source. +//! directory export maps the whole directory. The guest's `nvx::guest::mappings::MappingTable`, +//! whose entries come from an aggregate export, whose root lists its children, refuses an empty +//! source. use super::{ProtocolError, u16_at, u32_at, violation}; @@ -33,8 +35,8 @@ pub struct HostMap { pub read_only: bool, } -/// Encodes the host mapping table as the payloads of consecutive `APP_MAPS` requests, each at -/// most `limit` bytes long. +/// Encodes the host mapping table as consecutive `MAPS` payloads, each at most `limit` bytes +/// long, which the host sends in order, such as in NVX's own `APP_MAPS` requests. /// /// A payload holds the index of its first entry and its number of entries, so the guest agent /// applies the entries in order and only up to the number that the kernel command line diff --git a/nvx-rs/src/protocol/mod.rs b/nvx-rs/src/protocol/mod.rs index 2c1c406e4..6f5753e39 100644 --- a/nvx-rs/src/protocol/mod.rs +++ b/nvx-rs/src/protocol/mod.rs @@ -1,9 +1,8 @@ //! Codec of the OpenVMM control-console protocol. //! //! OpenVMM brokers an authenticated stream of outer records (magic `NVXS`) between one host -//! client and the guest's agent. Data records carry the bytes of an application protocol. NVX's -//! own, `NVXC`, with its frames and its `EXEC` payloads, is private to this crate: only NVX's -//! programs speak it, the CLI, nvx-init, the managed agent and the fake OpenVMM. All integers are +//! client and the guest's agent. Data records carry the bytes of an application protocol, which +//! the host and its guest choose: NVX's own is private to this crate. All integers are //! little-endian. //! //! - Outer records: [`encode_outer`] and [`decode_outer_header`], and the `OUTER_*` record types. diff --git a/scripts/module_diagrams.py b/scripts/module_diagrams.py index fa9e7e88d..fe3bc3d04 100644 --- a/scripts/module_diagrams.py +++ b/scripts/module_diagrams.py @@ -6,11 +6,12 @@ it: the module that contains each module, and the modules that each uses. It mistakes modules under some cfg conditions, such as cfg(not(windows)), for test modules, so this script also reads its analysis with cfg(test), and restores -them. It merges the exports of the platforms, draws with Graphviz the crate's -top-level modules, the dependencies between them, and the modules that each -contains, and writes the page that shows the diagrams. CI draws them after every -merge to dev that changes their inputs, and commits them when they change -(doc/ci.md, "Module diagrams"). +them. It keeps the modules of each platform's public API, without the hidden +modules of NVX's own programs, merges the platforms' exports, draws with +Graphviz the crate's top-level modules, the dependencies between them, and the +modules that each contains, and writes the page that shows the diagrams. CI +draws them every night, and commits them to dev when they change (doc/ci.md, +"Module diagrams"). """ from __future__ import annotations @@ -62,23 +63,35 @@ "core": ("#e0f2fe", "#0284c7"), "host": ("#ede9fe", "#8b5cf6"), "guest": ("#ccfbf1", "#0d9488"), - "testing": ("#fef3c7", "#d97706"), } -# What each top-level module holds, in the overview. +# What each top-level module of the public API holds, in the overview. DESCRIPTIONS = { "activation": "Layered root activation", - "contract": "Control compatibility", - "error": "Error types", - "exec": "Execution stdio and credit", + "contract": "Contract versions", "gpt": "GPT partition reader", - "guest": "Linux guest programs", - "host": "Launch and supervision", - "hypervisor": "Backend selection", + "guest": "Guest side", + "host": "VMs, snapshots, restores", "net": "IP networks", - "protocol": "NVXS / NVXC codecs", - "testing": "Fake OpenVMM", + "protocol": "NVXS and boot tokens", "uuid": "UUID encoding", } +# The modules of NVX's own programs, which the crate makes public only for the +# hidden entry points of its binaries: the CLI, the fake OpenVMM, and the guest +# programs. The diagrams leave them out, with the modules that they contain. +PROGRAMS = frozenset( + { + "nvx::guest::console_pending", + "nvx::guest::device_io", + "nvx::guest::exit", + "nvx::guest::ingress", + "nvx::guest::init", + "nvx::guest::managed_agent", + "nvx::guest::mmio_write", + "nvx::guest::time_probe", + "nvx::host::cli", + "nvx::testing", + } +) # Graphviz measures the labels with the first of these fonts that is installed, # and SVG viewers show them with the first that they have. CI installs # Liberation Sans, so that its layouts do not depend on the other fonts of its @@ -121,6 +134,9 @@ r"^(?P(?:│ | )*)[├└]── mod (?P[\w#]+): (?P.*)$" ) CONDITION = re.compile(r"#\[cfg\((?P.+?)\)\](?=, #\[cfg\(|$)") +# A condition that holds outside tests too once the export enables its feature, +# as any(test, feature = "cli") does. +TEST_OR_FEATURE = re.compile(r'any\(test, feature = "(?P[^"]+)"\)') class DiagramError(Exception): @@ -379,7 +395,9 @@ def restore( build outside tests are those of the analysis with cfg(test) but the test modules, so this restores the modules that the plain analysis lacks among them. It refuses conditions that depend on test otherwise, such as - any(test, unix) or not(test), whose modules it cannot tell apart. + any(test, unix) or not(test), whose modules it cannot tell apart, but for + any(test, feature = "cli") and its like, whose feature the export enables, and + which therefore holds outside tests too. The plain analysis moves the uses of a module that it drops, and those of the modules that use it, to the nearest module that contains it and that it @@ -399,6 +417,9 @@ def fail(reason: str) -> DiagramError: raise fail("its structure and its dependencies name different modules") for path, listed in found.items(): for condition in listed: + enabled = TEST_OR_FEATURE.fullmatch(condition) + if enabled is not None and enabled["feature"] in FEATURES: + continue # A string, such as the name of a feature, holds no condition. unquoted = re.sub(r'"[^"]*"', '""', condition) if condition != "test" and re.search(r"\btest\b", unquoted): @@ -431,6 +452,34 @@ def kept(path: str) -> str: ) +def public(export: Export) -> Export: + """The public API of one platform's export: each module that is public, as are + the modules that contain it, but NVX's programs and the modules that they + contain, and the uses between the modules that it keeps. It filters each + platform's export before the merge, so that a module that is public on one + platform alone is drawn as that platform's.""" + + def shown(path: str) -> bool: + parts = path.split("::") + return all( + export.visibility[module] == "pub" and module not in PROGRAMS + for module in ( + "::".join(parts[:depth]) for depth in range(2, len(parts) + 1) + ) + ) + + kept = {path for path in export.visibility if shown(path)} + return Export( + { + path: visibility + for path, visibility in export.visibility.items() + if path in kept + }, + {path: owner for path, owner in export.owner.items() if path in kept}, + frozenset(pair for pair in export.uses if kept.issuperset(pair)), + ) + + def merge(exports: Mapping[str, Export]) -> Crate: """Merges the exports of the platforms, in their order. A module whose visibility differs between them names the visibility of each.""" @@ -615,7 +664,8 @@ def diagrams(crate: Crate) -> list[Diagram]: " included, or whose items the definitions of its own items name," " which counts the implementations of a type's traits toward the" " type's module, wherever they are. The crate's own arrows are its" - " re-exports. The uses within a top-level module are left out.", + " re-exports. The uses within a top-level module, and those of the" + " modules that the diagrams leave out, are left out.", dependencies(crate), ), ] @@ -676,22 +726,23 @@ def link(text: str, path: Path, anchor: str = "") -> str: "# Module diagrams", "", *paragraph( - f"The modules of the {link('`nvx` crate', MANIFEST.parent / 'README.md')}," - " the modules that each contains, and the dependencies between them, as" - f" {tools}. They cover the crate's library with its {features}, and" - f" `{FEATURES[-1]}` features, on {targets}. CI draws them again after" - " every merge to `dev` that changes the crate, and commits them when" - f" they change; see {ci}." + "The public modules of the" + f" {link('`nvx` crate', MANIFEST.parent / 'README.md')}, the modules" + " that each contains, and the dependencies between them, as" + f" {tools}. They cover the crate's public API, as its library builds" + f" with its {features}, and `{FEATURES[-1]}` features, on {targets}:" + " each module that is public, as are the modules that contain it, but" + " the modules of NVX's own programs, which the crate hides. CI draws" + " them again every night, and commits them to `dev` when they change;" + f" see {ci}." ), *paragraph( - "Each box is a module, colored by the part of the crate that holds it:" - " blue for the modules that the host and the guest share, purple for" - " the host, teal for the guest, and amber for the test doubles. A solid" - " box is a public module, and a dashed box a private one, unless it" - " names another visibility, such as `pub(crate)`. A box that names" + "Each box is a public module, colored by the part of the crate that" + " holds it: blue for the modules that the host and the guest share," + " purple for the host, and teal for the guest. A box that names" " platforms, as in *Linux only*, is a module that only they build, as" " are the modules that it contains. The diagrams leave out the crate's" - " tests and binaries, and other crates." + " private modules, its tests and binaries, and other crates." ), ] hierarchies = False @@ -821,7 +872,7 @@ def draw(output: Path, dot: str, execute: Runner = run) -> list[str]: warn(execute(export_command(target, cfg_test=True), None)), target ) found = conditions(warn(execute(structure_command(target), None)), target) - exports[platform] = restore(plain, tested, found, target) + exports[platform] = public(restore(plain, tested, found, target)) crate = merge(exports) drawn = diagrams(crate) files: dict[str, str] = {} diff --git a/scripts/test_module_diagrams.py b/scripts/test_module_diagrams.py index b15af4a28..ad97ae929 100644 --- a/scripts/test_module_diagrams.py +++ b/scripts/test_module_diagrams.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 """Tests for module_diagrams.py, which draws the module diagrams of the nvx -crate that doc/modules publishes, and for the workflow that draws them after -every merge to dev and commits them. +crate that doc/modules publishes, and for the workflow that draws them every +night and commits them. They check how the script reads and checks cargo-modules' exports, which are -written here in its DOT form, how it merges the platforms' modules, the graphs -that it draws, the page that shows them, how it replaces an earlier run's -output, and what it runs, with a stand-in for cargo-modules and Graphviz. They -also check that doc/modules holds a page and the diagrams that it shows, the -workflow's triggers, credentials, and pins, and, on POSIX hosts, run its -publication step against a local repository. +written here in its DOT form, how it merges the platforms' modules, the modules +of the public API that it keeps, the graphs that it draws, the page that shows +them, how it replaces an earlier run's output, and what it runs, with a +stand-in for cargo-modules and Graphviz. They also check that doc/modules holds +a page and the diagrams that it shows, the workflow's triggers, credentials, and +pins, and, on POSIX hosts, run its publication step against a local repository. """ from __future__ import annotations @@ -381,6 +381,20 @@ def test_a_test_in_a_string_is_no_condition(self): restored = self.restore(found(imp=('feature = "test"',))) self.assertIn("nvx::host::imp", restored.visibility) + def test_a_test_or_a_feature_that_the_export_enables_builds_outside_tests(self): + # As the Linux platform's identity module, under cfg(any(test, feature = + # "cli")), which cargo-modules drops as a test module. + self.assertIn("cli", diagrams.FEATURES) + restored = self.restore(found(imp=('any(test, feature = "cli")',))) + self.assertIn("nvx::host::imp", restored.visibility) + # Without the feature, the module may build for tests alone. + other = 'any(test, feature = "other")' + with self.assertRaisesRegex( + diagrams.DiagramError, + re.escape(f"nvx::host::imp builds under cfg({other})"), + ): + self.restore(found(imp=(other,))) + def test_a_use_that_only_a_restored_module_makes_counts_once(self): # cargo-modules moves the uses of a module that it drops to the module # that contains it, so the plain analysis has host use protocol, which @@ -515,6 +529,59 @@ def test_labels_are_quoted_for_graphviz(self): self.assertEqual(diagrams.quote('a"b\\c\nd'), '"a\\"b\\\\c\\nd"') +class PublicTests(unittest.TestCase): + def test_the_public_api_leaves_out_private_modules_and_programs(self): + kept = diagrams.public( + diagrams.parse( + export( + { + "nvx": "crate", + "nvx::error": "pub(self)", + "nvx::host": "pub", + "nvx::host::cli": "pub", + "nvx::host::cli::args": "pub", + "nvx::host::sys": "pub(crate)", + "nvx::host::sys::fs": "pub", + "nvx::host::vm": "pub", + }, + ( + ("nvx", "nvx::error"), + ("nvx", "nvx::host::vm"), + ("nvx::host::cli", "nvx::host::vm"), + ("nvx::host::vm", "nvx::host::sys::fs"), + ), + ), + "linux", + ) + ) + # host::cli is a program's, and a public module inside it or inside a + # private module is no part of the API either. + self.assertIn("nvx::host::cli", diagrams.PROGRAMS) + self.assertEqual( + kept.visibility, + {"nvx": "crate", "nvx::host": "pub", "nvx::host::vm": "pub"}, + ) + self.assertEqual(kept.owner, {"nvx::host": "nvx", "nvx::host::vm": "nvx::host"}) + self.assertEqual(kept.uses, {("nvx", "nvx::host::vm")}) + + def test_a_module_public_on_one_platform_alone_is_that_platform_s(self): + modules = {"nvx": "crate", "nvx::a": "pub", "nvx::a::b": "pub"} + merged = diagrams.merge( + { + "Linux": diagrams.public(diagrams.parse(export(modules), "linux")), + "Windows": diagrams.public( + diagrams.parse( + export({**modules, "nvx::a": "pub(crate)"}), "windows" + ) + ), + } + ) + self.assertEqual(sorted(merged.modules), ["nvx", "nvx::a", "nvx::a::b"]) + self.assertEqual(merged.modules["nvx::a"].visibility, "pub") + self.assertEqual(merged.only("nvx::a"), "Linux") + self.assertIsNone(merged.only("nvx::a::b")) + + class GraphTests(unittest.TestCase): def test_boxes_show_their_visibility_and_platforms(self): source = diagrams.hierarchy(crate(), "nvx::host") @@ -570,7 +637,7 @@ def test_the_overview_shows_every_top_level_module_in_the_crate(self): ) self.assertEqual( label(drawn["nvx::error"]), - ["error", diagrams.DESCRIPTIONS["error"], "pub(crate)"], + ["error", "pub(crate)"], ) self.assertIn('subgraph "cluster_nvx"', source) self.assertEqual(edges(source), {}) @@ -672,7 +739,7 @@ def test_the_page_links_lead_from_its_directory_to_the_files(self): def test_the_page_leads_to_how_ci_draws_the_diagrams(self): text = " ".join(self.page().split()) - self.assertIn("CI draws them again after every merge to `dev`", text) + self.assertIn("CI draws them again every night", text) for output, target in ( (diagrams.DEFAULT_OUTPUT, "../../doc/ci.md#module-diagrams"), (PUBLISHED, "../ci.md#module-diagrams"), @@ -762,7 +829,16 @@ def test_draw_exports_each_platform_and_writes_the_page_and_diagrams(self): with tempfile.TemporaryDirectory() as directory: output = Path(directory) / "modules" names = self.draw(output, tools) - drawn = diagrams.diagrams(crate()) + drawn = diagrams.diagrams( + diagrams.merge( + { + platform: diagrams.public( + diagrams.parse(EXPORTS[platform], target) + ) + for platform, target in diagrams.TARGETS.items() + } + ) + ) self.assertEqual( names, sorted([diagrams.PAGE, *(diagram.name for diagram in drawn)]) ) @@ -789,15 +865,16 @@ def test_draw_exports_each_platform_and_writes_the_page_and_diagrams(self): self.assertIn("cargo-modules) 9.9.9 finds them and Graphviz 1.2.3 draws", flat) def test_draw_restores_the_modules_that_cargo_modules_drops_as_tests(self): - # As nvx::host::sandbox::scratch::imp: each platform builds a module of - # the same path under its own condition, but cargo-modules drops Linux's, - # under cfg(not(windows)), unless it analyzes with cfg(test). + # As nvx::host::sandbox::scratch::imp, if it were public, as the modules + # that the diagrams draw are: each platform builds a module of the same + # path under its own condition, but cargo-modules drops Linux's, under + # cfg(not(windows)), unless it analyzes with cfg(test). linux = { **LINUX_MODULES, - "nvx::host::imp": "pub(self)", + "nvx::host::imp": "pub", "nvx::host::tests": "pub(self)", } - windows = {**WINDOWS_MODULES, "nvx::host::imp": "pub(self)"} + windows = {**WINDOWS_MODULES, "nvx::host::imp": "pub"} imports = (*LINUX_USES, ("nvx::host::imp", "nvx::error")) tools = FakeTools( {LINUX: EXPORTS[LINUX], WINDOWS: export(windows, WINDOWS_USES)},