Commit Graph
82 Commits
Author SHA1 Message Date
Michael Crosby 872f601f93 add hotplug interfaces for vmms (#740)
Signed-off-by: michael_crosby <michael_crosby@apple.com>
2026-05-18 10:29:36 -07:00
jwhur f8a18e8dcd Make maxIDLength public (#734) 2026-05-11 16:31:30 -07:00
Danny Canter ece8f984b3 vminitd: Fix UDS length issue with large container IDs (#731)
Today we craft the proxied sockets we create at a path that contains the
container ID. This is normally completely fine, except if the ID is
enormous, as it can cause the UDS path to be longer than the allowed
length.
2026-05-11 15:26:31 -07:00
Danny Canter 737bd37777 LinuxContainer/LinuxPod: Add ID length restriction (#733)
I think realistically we should have a limit on this. Technically today
the ID length restriction is whatever our grpc buffer length is..

I think 64 is long enough so that folks could use an entire hex based
sha256, any uuid, and also sethostname has a length restriction of 64
anyways.
2026-05-11 14:47:20 -07:00
Yibo Zhuang 03280f3952 refactor memory alignment to VZVirtualMachineInstance (#725)
Move the MiB rounding from LinuxContainer into
`VZVirtualMachineInstance`, so all callers are covered in one place and
the alignment constraint stays with the VMM layer that owns it.

This will also ensure LinuxPod can get memory alignment for free.
2026-05-06 07:36:10 -07:00
Danny Canter 0e469481f5 LinuxContainer: Allow cpu/mem overhead to be configurable (#710)
If someone wanted to use a custom guest agent/guest image in general the
overhead we chose is kind of a nuisance.. This allows the values to be
configurable, and raises the default to 128, and vminitds cgroup
monitoring to 80.
2026-04-29 10:33:44 -04:00
Dmitry Kovba 3ef9976d38 Fix compilation warnings (#696)
Fix compilation warnings:

```
.../Sources/Containerization/LinuxContainer.swift:877:35: warning: 'weak' ownership of capture 'self' differs from implicitly-captured strong reference in outer scope [#ImplicitStrongCapture]
 851 |     /// via the `start` method.
 852 |     public func exec(_ id: String, configuration: @Sendable @escaping (inout LinuxProcessConfiguration) throws -> Void) async throws -> LinuxProcess {
 853 |         try await self.state.withLock { state in
     |                                       |- note: 'self' implicitly strongly captured here
     |                                       `- note: add 'self' as a capture list item to silence
 854 |             var startedState = try state.startedState("exec")
 855 | 
     :
 875 |                 vm: startedState.vm,
 876 |                 logger: self.logger,
 877 |                 onDelete: { [weak self] in
     |                                   |- warning: 'weak' ownership of capture 'self' differs from implicitly-captured strong reference in outer scope [#ImplicitStrongCapture]
     |                                   `- note: explicitly assign the capture list item to silence
 878 |                     await self?.removeProcess(id: id)
 879 |                 }

.../Sources/Containerization/LinuxContainer.swift:914:35: warning: 'weak' ownership of capture 'self' differs from implicitly-captured strong reference in outer scope [#ImplicitStrongCapture]
 890 |     /// via the `start` method.
 891 |     public func exec(_ id: String, configuration: LinuxProcessConfiguration) async throws -> LinuxProcess {
 892 |         try await self.state.withLock {
     |                                       |- note: 'self' implicitly strongly captured here
     |                                       `- note: add 'self' as a capture list item to silence
 893 |             var state = try $0.startedState("exec")
 894 | 
     :
 912 |                 vm: state.vm,
 913 |                 logger: self.logger,
 914 |                 onDelete: { [weak self] in
     |                                   |- warning: 'weak' ownership of capture 'self' differs from implicitly-captured strong reference in outer scope [#ImplicitStrongCapture]
     |                                   `- note: explicitly assign the capture list item to silence
 915 |                     await self?.removeProcess(id: id)
 916 |                 }

[#ImplicitStrongCapture]: <https://docs.swift.org/compiler/documentation/diagnostics/implicit-strong-capture>
```
2026-04-28 11:18:13 -04:00
Danny Canter 56f1c83a14 Rework signals usage/parsing (#704)
Replace the old platform-gated Signals helper in ContainerizationOS with
a Signal struct in Containerization. Signal provides named constants for
common signals (.kill, .term, etc.), platform-specific namespaces
(Signal.Linux for rtmin/stkflt/pwr, Signal.Darwin for emt/info), string
parsing, and ExpressibleByIntegerLiteral support.

LinuxContainer, LinuxProcess, and LinuxPod kill APIs now take Signal
instead of raw Int32.
2026-04-28 11:15:13 -04:00
Danny Canter 46c8fd3269 LinuxContainer/LinuxPod: Expose withVirtualMachineInstance (#701) 2026-04-27 12:17:12 -07:00
Dmitry Kovba 41ffd07f3e Add a default route to the first interface only (#697)
Adds a default route to the first interface only.
2026-04-23 11:05:25 -04:00
Danny Canter 47518e0738 LinuxContainer: Give 1 vcpu of overhead for guest (#684) 2026-04-14 00:19:40 -07:00
Danny Canter ceee3a67ba vminitd: Lower nio event loop group threads to 1 (#677) 2026-04-10 10:47:22 -07:00
Danny Canter ab89983c56 Mounts: Change single file mounts to a different approach (#665)
Related to https://github.com/apple/container/issues/1251

Change to sharing in the parent directory, and then bind mounting in the
file into the container. This has the unfortunate reality of being less
secure, but the current approach is burdended by a couple things,
namely:

1. You can't share in files that are on a different volume
2. There is a Virtualization bug that causes spurious errors when trying
to open the file.

I've added a doc to go over the approach we've taken, and some
workarounds if the approach is not satisfactory.
2026-04-08 22:01:13 -07:00
Danny Canter 3c4215702b LinuxContainer/LinuxPod: Sort mounts by depth (#601) 2026-04-03 10:28:47 -07:00
Dmitry Kovba 11bbbbb852 Support a default route without a gateway (#628)
Adds the support for a default route without a gateway.

```
/ # ip route show default
default dev eth0 scope link 
```
2026-04-02 14:27:56 -07:00
Danny Canter 8615e3b021 Remove more incorrect os gates (#632)
A lot of logic we have works perfectly fine on other OS'.
2026-04-02 10:12:55 -07:00
Danny Canter 4c28821fef LinuxContainer: Move memory clamping to VZ code (#630)
The memory limit (clamp to system ram) is a VZ limitation so it's weird
that we had that logic directly in LinuxContainer. Someone could make a
VZVirtualMachineInstance that doesn't have this limitation but we
inherit this logic because it's done before passing off the memory
amount to the VMM.
2026-04-01 20:17:14 -07:00
Danny Canter 220042089b Remove LinuxContainer.setupIO (#627)
Dead code
2026-04-01 13:57:43 -07:00
Danny Canter 1be13d1d76 LinuxContainer: Support copying directories (#571)
Now that the static linux SDK we use has libarchive linked against it,
we can finally live our dream (/s) of supporting copying directories
in/out a little easier. This was.. kind of annoying. I think the very
simple route of writing archive to guest/host -> streaming over grpc is
simplest, but on the guest end we have a couple problems:

1. The VMs rootfs is read only today always, which is a good thing to me
and I don't want to change if we don't have to.
2. Writing the archive to the containers rootfs temporarily could work,
but it's a bit weird, and the user can make the containers rootfs
readonly which would screw that plan.
3. We could write it to /run or /tmp, but they're tmpfs and dealing with
the headache of the user possibly tarring an enormous dir is one I don't
want to care about.

So, that leaves us with the (truthfully better to me) approach of trying
to write the tar data directly to the host and skipping grpc which kinda
forces us to have a temp spot. Because of that, I made it such that we
pass a port number from host<->guest, and transfer the actual data
(either the single file or tarred dir contents) over the vsock port
instead. The stream is kinda clunky, but it just serves as a means to
exchange metadata and a "we're done" signifier.
2026-03-06 15:30:00 -08:00
Danny Canter 6f9ae02933 LinuxContainer/LinuxPod: Fix UDS proxy if target is symlink (#568)
Closes #256

If the target of the uds proxy goes through a symlink (/var/run) we had
the same bug we had with mounts previously where we'd follow this and if
the resolved path existed in the root of the VM we'd end up mounting
there. Fix this by plopping the sockets in a holding spot and then bind
mounting them in (which has the correct resolution logic).
2026-03-03 10:41:28 -08:00
Danny Canter 205ccc7774 LinuxPod: Add hostname field on pod config (#561)
Just like we have for hosts and dns configurations, we can add a
hostname field to make it easier to setup if every container is going to
be using the same hostname.
2026-02-26 13:41:40 -08:00
Danny Canter 32cf8c4d32 vminitd: Implement minimal init process to reap zombies (#537)
There are some workloads that benefit from having a "true" init process
that reaps zombies. This change implements a minimal init as part of
vminitd and exposes an API to be able to use this init process in our
containers. signals will be forwarded, any child procs will be reaped
and the exit code of the actual workload will be propagated. The
LinuxContainer/LinuxPod API is a very simple bool to ask to use this as
our init process, and the init simply spawns whatever command you want
as a child underneath this.

```
➜  ./bin/cctl run --kernel bin/kernel.arm64 --init
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /.cz-init -- /bin/sh
    2 root      0:00 /bin/sh
    3 root      0:00 ps aux
```
2026-02-20 16:22:36 -08:00
Danny Canter 061bc0a071 LinuxContainer: Add support for optional writable layer (#533)
This adds support to LinuxContainer to be able to provide an optional
writable layer. This is useful as today the rootfs size is determined by
the size of the image block itself which is set in stone at unpack time
of the image. This leaves a lot to be desired because a user might want
a larger or smaller rootfs size, but it's not configurable on a per
container basis because of this. To support this, we can pass an
additional writable block device that we can overlayfs with the image
contents in the guest. All writes will go to this writable layer, and
the rootfs size in the container is now whatever size this writable
layer is.

The block devices (via our ext4 package, but you could use whatever) are
cheap enough to generate on the fly that you can choose whatever size
you want easily.
2026-02-19 15:27:43 -08:00
Anthony df5f0f336a Fix usage of "clean up" as verb (#507)
"clean up" is a verb, and "cleanup" is a noun. There are several
examples of the incorrect usage of "cleanup" as a verb in this
repository, including one in the public API (`cleanupOrphanedBlobs`).
For this public method, I've added a deprecation notice to the name with
the incorrect usage.
2026-02-04 09:14:25 -08:00
jwhur fd62f311b4 Shutdown VM gracefully from created state (#495)
This PR enables gracefully shutting down VM even when the
`LinuxContainer` is in `created` state.
2026-01-26 15:56:24 -08:00
1a017417bc LinuxContainer/LinuxPod: Single file support (#487)
Fixes #79

This adds support for single file (virtiofs based) mounts to the two
main container types. This is transparent, and doesn't need to be setup
by a user, so there is no visible API change. Virtiofs does not support
single file mounts today, and it would be less than ideal from a
security standpoint to just expose the parent directory of the file to
the vm and then bind mount in the file, so we chose the following:

1. Create a temp directory the container types will manage.
2. Hardlink in the file we want.
3. Share that tempdir into the vm.
4. Finally bind mount in the file into the container.

The main goal I wanted for this is to leave the logic out of the
`VirtualMachineInstance`. I really want this to just be a little dance
the container types do, and leave the vm out of it. Because of that,
most of the logic is shoved in a new `FileMountContext` type that does
the dirty tricks of rewriting what the user asked for to be a bind mount
from a temporary holding spot we'll use in the guest for these temp
directories.

One of the downsides to this is today the tempdirs are solely on the
main volume, so cross volume will need to copy the file to the tempdir,
so writes won't get written back.

Co-authored-by: Dean Coulstock <d_coulstock@apple.com>
Co-authored-by: Jaewon Hur <hurjaewon@snu.ac.kr>
2026-01-22 20:57:58 -08:00
Dmitry Kovba 1d9140cc8a Add a route when a gateway is outside a subnet (#488)
When we set a gateway outside a subnet in Linux, this fails with:
```
ip-route-add-default: netlink response indicates error, rc = -101 
```

To resolve the error, we should first add a route to the gateway outside
the subnet, which this PR does.
2026-01-22 18:57:46 -08:00
Danny Canter 863380c5e1 Interface: Support custom mtu (#484)
We started the legwork awhile ago, but never exposed it to be
configurable by the container types.
2026-01-20 17:32:28 -08:00
Danny Canter bbe4159649 LinuxContainer: Give a bit of overhead memory (#472)
The guest agent isn't free, and today we were sizing the VMs memory
allotment and the containers cgroup to the same value. This change gives
some overhead memory to the VM (50MB for now) for the guest agent.
2026-01-14 13:31:34 -08:00
Danny Canter 8446f895ee Allow filtering container statistics (#471)
It's possible a user doesn't want the full stats list, and only wants
cpu/mem etc. This plumbs through the ability to filter to only what is
requested. This, while we're already here, adds in memory.event output
to the stats list. For that specifically, I think eventually we may want
a streaming variant of this so you can get alerted of changes in the
file immediately instead of polling/one off reads, but this is useful
for now.
2026-01-13 13:35:00 -08:00
Danny Canter 2f55d75fec Support read only rootfs (#461) 2026-01-02 13:28:44 -08:00
Danny Canter 44ecb87f8d Agent: Add single file copying (#463)
Add a streaming rpc to copy single files in and out. This can easily be
extended to support copying directories, but we need the guest agent to
be able to tar/untar, and we'll need to figure out what that model is.
2026-01-02 08:57:40 -08:00
J LoganandAgam Dua 9ba8267afb Use typesafe IP/CIDR parameters everywhere. (#448)
- Closes #445.
- Adopts refined IPv4 and IPv6 types developed
  by Agam Dua <agamdua@users.noreply.github.com>.
- For type safety and clarity, use IP and CIDR types
  where we were previously using String.

Co-authored-by: Agam Dua <agamdua@users.noreply.github.com>
2025-12-16 09:58:44 -03:00
Danny Canter 836b699a91 Wire up experimental OCI runtime support (#416) 2025-11-21 12:16:58 -08:00
Danny Canter 85a3544750 LinuxContainer: "Harden" stop (#388) 2025-11-16 14:13:41 -08:00
Danny Canter 4c761d50c6 Add new FileHandle option for serial console output (#410)
Taking in a filehandle gives the user quite a bit more freedom on how to
handle boot log output. They can set up a kqueue watch on it and
redirect output somewhere else etc etc. The implementation for this has
us take in a new BootLog type that has two options:

1. .file, which is analogous to what we had prior. Just provide a URL
and a true by default append field.
2. .fileHandle which is the new addition. Can pass any fd that is
writable, and the VMM should write serial console output to it.
2025-11-14 10:40:32 -08:00
Danny Canter c8549315ed LinuxContainer: Keep reference to vended execs (#408)
This change is aimed at making forgetting to call .delete() on a
LinuxProcess less destructive than it can be. Because Virt.framework
invalidates any vsock fds it vended if the vm is stopped, trying to
perform some operations on the grpc client through any of the process
methods could trigger an ebadf, which NIO asserts on. This keeps a
reference to the execs and deletes all of them for you once the
container dies. I still think leaving .delete a public method is useful
as otherwise the stdio fds are left open, but cleanup should occur all
in one place now if you don't care about this.

This additionally:

1. Fixes two of our tests that forgot to delete() an exec.
2. Adds two new tests to verify that process.delete() is now idempotent,
and we don't need to call delete().
2025-11-13 02:13:09 -08:00
Danny Canter 01437a44a2 Vminitd: Sync(2) on shutdown (#400) 2025-11-12 09:39:46 -08:00
Danny Canter 8a9b0deb82 LinuxProcess: Swap from logging in delete (#386)
This changes LinuxProcess to not just log errors but return them.
2025-11-11 14:42:41 -08:00
Danny Canter f3d998975c LinuxContainer: Add new constructor (#377)
Add a constructor that allows just passing in a configuration without
doing the closure dance.
2025-10-31 14:27:39 -07:00
Danny Canter df8b97a9f7 Chore: Use guestVsockPorts (#373)
This was a funny oversight. We have two port spaces, one for listening
sockets on the host, and one for vsock proxies in the guest, but only
the host "allocator" was being used. This didn't really matter as the
ports in the guest would still be unique, but would still be good to
fix.
2025-10-29 16:44:28 -07:00
Danny Canter eb1b8741b5 LinuxContainer/Pod: Remove pause/resume (#366) 2025-10-27 17:33:49 -07:00
Danny Canter f697215e54 LinuxContainer: Stop all relays on create failure (#358)
If create fails we don't cleanup the relays we may have setup.
2025-10-24 14:48:26 -07:00
J Logan 6f7601e475 Handle EOF on one side of a UnixSocketRelay. (#323)
- Current implementation shuts down everything as soon as EOF is
detected on DispatchSourceRead for the relay. This is problematic for,
say an HTTP request where the client makes a request, and calls
`shutdown(fd, SHUT_WR)` to close the send side, but it expects to be
able to keep calling `recv()` to get the response.
- Changed cancel handlers so that the one that sees that both sources
have been cancelled then closes both the UDS and vsock fds.
- Updated vminitd VsockProxy to also do `shutdown(fd, SHUT_WR)` for read
hangup or EOF, and only close the underlying fds and unwire pollers on
full hangup, broken pipe, error, or when both sides half-close.
2025-10-24 13:13:13 -07:00
Danny Canter dc4284a149 Introduce new experimental LinuxPod type (#343)
Closes #319

Introduces a new type capable of running > 1 container in the guest. The
API mostly follows LinuxContainer, and each individual container can be
addressed via any of the methods that require you to pass the
containerID as the first param. Today there's no support for namespace
sharing, but that shouldn't be terrible to support.
2025-10-23 13:59:58 -07:00
Danny Canter 22f7b6ac13 LinuxContainer: Add bootlog as configuration field (#344)
Closes #227

Previously, the bootlog was supplied once in the constructor to
VZVirtualMachineManager which meant that if you used this same manager
for multiple ctrs that all logs would end up going to the same file,
which becomes quite cumbersome to follow..

This change moves bootlog to be a container configuration param and also
moves it to be a VMConfiguration param, so it can be threaded through
from LinuxContainer -> vmm.create() and be truly container unique now.
The largest driver for this was the integration tests which today every
single test spits out logs to a singular file, making guest
investigations tricky to actually look into.

Result after:
```
➜  containerization git:(bootlog-per-ctr) ✗ ls -alh bin/bootlogs
total 1520
drwxr-xr-x@ 24 dcantah  staff   768B Oct 22 17:34 .
drwxr-xr-x@  8 dcantah  staff   256B Oct 22 17:34 ..
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-cat-mount.log
-rw-------@  1 dcantah  staff    22K Oct 22 17:34 test-cgroup-limits.log
-rw-------@  1 dcantah  staff   249K Oct 22 17:34 test-concurrent-processes-output-stress.log
-rw-------@  1 dcantah  staff   167K Oct 22 17:34 test-concurrent-processes.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-devconsole.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-hostname.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-hosts-file.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-manager.log
-rw-------@  1 dcantah  staff    22K Oct 22 17:34 test-container-reuse.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-statistics.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-container-stdin.log
-rw-------@  1 dcantah  staff     0B Oct 22 17:34 test-nested-virt.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-pause-resume-io.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-pause-resume-wait.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-pause-resume.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-custom-home-envvar.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-echo-hi.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-false.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-home-envvar.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-true.log
-rw-------@  1 dcantah  staff    11K Oct 22 17:34 test-process-tty-envvar.log
-rw-------@  1 dcantah  staff    38K Oct 22 17:34 test-process-user.log
```
2025-10-23 03:27:24 -07:00
Danny Canter 056a909081 VirtualMachineManager: Rework protocol (#341)
Today this protocols create method is just odd. Our only implementation
of it immediately casts to LinuxContainer, failing if it cannot do so. I
think what might make more sense is to pass in a configuration itself
with core parameters that we expect every vmm to be able to support, and
then in a specific implementation they can continue to cast this type to
a specific one to possibly extract some extra configuration values
(rosetta for VZ for example).

This rework will also make it simpler to support a Pod type, as the vm
setup is identical and simple.
2025-10-21 01:23:00 -07:00
Danny Canter 4f996d3509 LinuxContainer: Move Process type out of Configuration (#340)
As a first step to making a pod type, move the nested process
configuration out of LinuxContainer.Configuration.Process and into a
standalone LinuxProcessConfiguration type.
2025-10-20 10:52:48 -07:00
Kathryn Baldauf 09da9205c6 Make vmm create protocol async (#338)
The `vmm.create` call is already made within an async function. This PR
updates the protocol to allow other vmm implementations more flexibility
in `create`.

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
2025-10-17 14:54:57 -07:00
Danny Canter d0383eb5bf LinuxContainer: AsyncMutex-ify LinuxContainer (#325)
This change moves LinuxContainer to use the new AsyncMutex type. This allows us to keep using our old state machine model, but lock around the entirety of operations. This also allows us to axe quite a few states that were intermediary states only there to protect against multiple threads calling (even if it was unlikely..) the same method at the same time (.creating, .starting, .stopping etc.).

The rationale is: Mutex is somewhat tricky to use as you can't (for good reason) do any async work inside the closure. This makes some of our work difficult as every single interaction with the guest is via an rpc, which is all async. There really isn't a worry about contention, because most container methods will only ever be invoked once (create, start, stop) so a big heavy lock isn't much of a deterrent.
2025-10-09 15:09:53 -07:00