Commit Graph
98 Commits
Author SHA1 Message Date
James Stringer 194cadeaa6 NVENC/NVDEC userspace path + nvidia-smi shortcut
- systemd-user environment generator and appsandbox-gpu wrapper both
  append /usr/lib/wsl/lib to LD_LIBRARY_PATH. ldconfig has the .so.1
  entries cached, but the 9P mount is read-only so the unversioned
  symlinks ldconfig wants to create can't be written. dlopen() of
  unversioned names (libcuda.so, libnvidia-encode.so, libnvcuvid.so)
  needs the path on LD_LIBRARY_PATH — without it ffmpeg's h264_nvenc /
  h264_cuvid plugins fail with 'Cannot load libcuda.so.1'.

- setup.sh: symlink /usr/lib/wsl/lib/nvidia-smi to /usr/local/bin so
  the bare command works without env tweaks. Created unconditionally
  because the symlink target is resolved at exec time — the file
  appears under the agent's 9P mount on first boot regardless of when
  the symlink was created.
2026-05-19 22:09:52 -04:00
James Stringer 2309cec029 Add Linux VM support with GPU-PV
Ubuntu 26.04 LTS guest end-to-end:

- cidata-driven autoinstall pipeline (subiquity user-data + nested
  cloud-init bootcmd) in disk_util.c. Static NAT IP, ssh-via-vsock
  marker, serial-debug kernel cmdline injected into ESP grub.cfg by
  iso-patch.exe. Five separate firstboot pipeline fixes get the VM
  to GDM unattended on Hyper-V Gen 2.

- Linux guest stack under tools/linux/:
    - appsandbox-agent  (vsock :1 control + clipboard helper spawn)
    - appsandbox-audio  (snd-aloop -> vsock :4)
    - appsandbox-display (KMS framebuffer capture -> vsock :2)
    - appsandbox-input  (uinput injection <- vsock :3)
    - appsandbox-clipboard (XCB selection bridge <-> vsock :5/:6)
  Helper binaries staged via release/resources/linux/.

- asb_drm DRM module: virtual single-CRTC display sink the host
  captures over vsock. Platform-bus, DRIVER_GEM/MODESET/ATOMIC/RENDER,
  linear shmem framebuffers. asb-evict-simpledrm.service unbinds the
  EFI simple-framebuffer before display-manager so mutter binds
  asb_drm as the sole DRM card.

- dxgkrnl vendored byte-identical from microsoft/WSL2-Linux-Kernel
  linux-msft-wsl-6.18.y with our DKMS/out-of-tree shim (dxgkrnl_compat.h
  + Kbuild + dkms.conf). Autoinstall late-commands dkms-builds against
  every /target/lib/modules/<kver> so kernel-version drift between ISO
  and post-update reboot still leaves a working .ko.

- wsl-mesa custom build (Mesa 25.3 + d3d12 gallium + microsoft-
  experimental dzn Vulkan). Installs to /opt/wsl-mesa parallel to
  Ubuntu's stock Mesa. Prebuilt tarball for ubuntu-26.04-amd64
  shipped under tools/linux/wsl-mesa/prebuilt/ (21 MB).

- Per-VM GPU acceleration policy:
    - /etc/systemd/user-environment-generators/50-appsandbox-gpu
      emits GALLIUM_DRIVER=d3d12 + LD_LIBRARY_PATH + VK_DRIVER_FILES
      into the user@1000 systemd-user environment when /dev/dxg and
      /opt/wsl-mesa are present. Inherited by every app-*.scope a
      .desktop launch creates.
    - /etc/systemd/user/org.gnome.Shell@.service.d/no-gpu.conf
      UnsetEnvironment= strips these for the compositor — mutter
      stays on llvmpipe so asb_drm scanout works (the d3d12 pipe has
      no GBM compatibility with asb_drm).
    - /usr/local/bin/appsandbox-gpu wrapper for terminal-launched
      opt-in GPU acceleration (probes /dev/dxg; no-op on no-GPU VMs).

- Host changes:
    - vm_display_idd.c, vm_clipboard.c, vm_agent.c, vm_ssh_proxy.c,
      asb_display.c: hcs_service_guid(os_type, port) so the same code
      paths reach the per-OS service identifiers (HV_GUID for Windows,
      vsock template for Linux).
    - asb_core.c: linux_create_thread builds an empty VHDX, cidata
      ISO with extras (agent ELFs, dxgkrnl src, asb_drm src, wsl-mesa
      tarball, wsl-deps libs), and boots the desktop ISO + cidata.
    - hcs_vm.c: HostLxssLib Plan9 share appended for Linux VMs;
      VideoMonitor + ComPorts retained for serial debug.
    - gpu_enum.c/h: gpu_append_lxsslib_share helper.
    - vm_clipboard.c file-drop support (CF_HDROP / x-special/gnome-
      copied-files round-trip).
    - vm_display_idd.c: 32-bit S32_LE PCM audio path matches the
      Linux daemon's snd-aloop capture format.

- iso-patch.c gains a patch_ubuntu_esp_grub_for_debug pass that
  rewrites /EFI/ubuntu/grub.cfg via the FAT32 ESP, plus a qcow2->
  vhdx converter with a RFC 1951 DEFLATE inflater for compressed
  cluster handling.

- wsl-deps: Microsoft's libd3d12.so / libd3d12core.so / libdxcore.so
  fetched on demand from the public NuGet feed
  pkgs.dev.azure.com/shine-oss/wsl (no auth). Staged at
  /opt/appsandbox/wsl-deps on the guest and put on /etc/ld.so.conf.d
  rather than /usr/lib/wsl/lib (which is overlaid by the agent's
  9P mount).

UI: web/{app.js,index.html} expose Linux VM creation + GPU selection,
distribution dropdown for cached Ubuntu ISOs, and the ISO picker for
custom images.
2026-05-19 20:21:43 -04:00
James Stringer 6e8f7e4006 Merge pull request #2 from jamesstringer90/win-net-improvements
Make Windows NAT VM creation more reliable + build fixes
2026-05-17 12:42:58 -04:00
James Stringer f8ec287963 Delete orphan HCN endpoint when VM creation fails; retry at all sites
Two related bugs surfaced by the user's create-fails-then-retry sequence:

1. When hcs_create_vm_with_endpoint fails after we already created the
   HCN endpoint (e.g. WBEM_E_INVALID_PARAMETER from a transient HCS
   error, or insufficient RAM surfaced through HCS), the endpoint was
   never deleted. The IP reservation lingered in HNS even though the
   endpoint owner VM never came into existence. The next attempt at the
   same IP failed with HCN_E_ADDR_INVALID_OR_RESERVED (0x803B002F).

   Fixed by deleting the endpoint in every HCS-create or hcs_start_vm
   failure path that follows a successful endpoint create, across all
   three call sites (start_vm_thread, vhdx_create_thread, asb_vm_create).

2. Master had the retry-on-RESERVED loop only at start_vm_thread.
   vhdx_create_thread and asb_vm_create created endpoints with no
   retry, so even when the phantom reservation existed, those paths
   silently fell back to creating the VM without a network.

   Factored the retry into try_endpoint_with_retry() and used it at
   all three call sites.
2026-05-17 12:31:21 -04:00
James Stringer 5081b68dae Stable-ID handles for long-lived per-VM threads
Root-cause fix for the bug that dc3fefa band-aided: g_vms[] is compacted
(shift+ZeroMemory) when a VM or template at a lower index is removed,
which silently invalidates any background thread's cached VmInstance*.

Affected threads were the agent connection (vm_agent.c::agent_thread_proc)
and the IDD probe (asb_core.c::idd_probe_thread_proc). The probe also
posted a stale VmInstance* to the UI via WM_VM_IDD_READY.

Fix:

* VmInstance gains a UINT64 unique_id, set on creation from a process-
  global monotonic counter, never reused. The id rides along when an
  entry is shifted (it's part of the struct that gets memcpy'd) and is
  zeroed when a slot is freed. asb_find_vm_by_id() returns the current
  &g_vms[i] for that id or NULL if the VM was deleted.

* AgentConn now stores vm_id instead of vm. agent_thread_proc resolves
  via asb_find_vm_by_id at every iteration; loop exits cleanly when
  the id no longer maps to a live VM.

* idd_probe_thread_proc is launched with the id (cast through ULONG_PTR)
  and resolves every iteration the same way. WM_VM_IDD_READY now
  carries the id in LPARAM; the UI handler resolves it freshly.

Obsoletes dc3fefa's symptom-level name-guard, which is reverted in the
next commit.
2026-05-17 12:12:32 -04:00
James Stringer b8c35d51b4 Use static 192.168.42.0/24 NAT subnet (with 1 fallback candidate)
Replaces master's hardcoded 172.20.0.0/16, which overlapped the
built-in Default Switch (172.20.48.0/20) on every Windows 11 host.

The chosen subnet is decided once per process from a tiny static
candidate list:

    192.168.42.0/24  (default)
    192.168.142.0/24 (fallback if .42 conflicts with a host adapter)

Picked by collect_inuse_subnets() + ranges_overlap() at first call,
stable for process lifetime. If both candidates collide with a host
adapter (rare), we fall through to the first and log a clear warning
pointing at the candidate list in hcn_network.c.

No saved-state coordination, no per-VM seed, no HCN endpoint sweep --
master's startup network-delete (hcn_cleanup_stale_networks) wipes the
prior run's network so the freshly-picked subnet is always honored.

Replaces the over-engineered runtime picker that was reverted in the
previous two commits.
2026-05-17 12:04:37 -04:00
James Stringer c0afcba3f3 Revert "Auto-pick NAT subnet at runtime to avoid host-adapter overlap"
This reverts commit afab17b9e0.
2026-05-17 12:01:04 -04:00
James Stringer 9189c9e77d Revert "Stop deleting HCN networks at startup; sweep stale endpoints instead"
This reverts commit 423b9a75a4.
2026-05-17 12:00:58 -04:00
James Stringer dc3fefa3f5 Guard agent/probe threads against zeroed g_vms[] slots
When a VM or template at a lower index is removed (deleted, or template
finishes sysprep), the g_vms[] array is compacted: entries shift down
and the now-vacated last slot is zeroed. Any background thread that
cached its VM pointer as &g_vms[idx] before the shift is left pointing
at the wrong slot (either zeroed or holding another VM's data).

Concrete repro: create a template, then create a regular VM while the
template is still building. The regular VM's agent thread holds
&g_vms[1]. When the template at g_vms[0] finishes and gets removed,
the regular VM's data shifts to g_vms[0] and &g_vms[1] is zeroed. The
agent thread now loops every few seconds calling
hcs_find_runtime_id(L"", ...) -- which logs the symptom the user saw:

  HCS enumerate: [{...running VM...}]
  hcs_find_runtime_id: VM "" not found in enumeration

Surgical fix: the agent loop and IDD-probe loop both bail out cleanly
when vm->name[0] is empty, which is the post-zero state. Stops the
log spam and the wasted IOCTLs.

This is a defensive guard, not a root-cause fix. The deeper issue is
that g_vms[] uses unstable indices: any cached VmInstance* can be
invalidated by compaction. A proper fix replaces shift+truncate with
stable slots marked as deleted, which is a larger refactor.
2026-05-17 11:46:19 -04:00
James Stringer 423b9a75a4 Stop deleting HCN networks at startup; sweep stale endpoints instead
Previously, hcn_cleanup_stale_networks() at app init blindly deleted our
three fixed-GUID networks. That had two bad effects:

1. The vEthernet adapter for AppSandboxNAT was destroyed on every start,
   forcing a re-pick of the subnet. If a saved VM existed (with a
   previously-allocated 192.168.x.y), and the picker -- not knowing the
   old subnet -- happened to choose a different one, the new endpoint
   create would fail with HCN_E_ADDR_INVALID_OR_RESERVED because the
   IP wasn't in the live network's range.

2. asb_cleanup() at app exit terminates VMs but does not call
   asb_vm_cleanup_network. It relied on the async HCS SystemExited
   callback to fire asb_hcs_state_changed -> asb_vm_cleanup_network for
   each VM, but the process often exited before the callback completed.
   The startup network-delete was the safety net catching the resulting
   orphans -- which is exactly the destructive behavior in (1).

Replaced with:

* hcn_cleanup_stale_endpoints() at startup. Enumerates every HCN endpoint,
  uses HcnQueryEndpointProperties to verify it belongs to one of our
  three networks (so we never touch foreign endpoints), and deletes
  just those. Networks themselves are left intact.

* hcn_seed_nat_subnet_from_ip() called from asb_init after load_vm_list
  with the first saved VM's IP. The picker adopts that subnet so the
  globals match the live network across restarts.

* asb_cleanup now calls asb_vm_cleanup_network for each VM after
  hcs_terminate_vm. This is the reference-counted teardown that already
  existed for clean per-VM stops; it deletes the endpoint and (only when
  no other VM uses the shared network) the network. The adapter goes
  away cleanly on clean exit and the next startup will pick a fresh
  subnet -- no leak.

* try_create_endpoint_with_retry() factors the increment-on-fail loop
  out of start_vm_thread and applies it to the two other endpoint-create
  sites (vhdx_create_thread, asb_vm_create). Defense in depth in case
  HNS ever holds a phantom reservation past the endpoint sweep.
2026-05-17 11:33:27 -04:00
James Stringer 93a0722214 Vendor WebView2Loader binaries so fresh clones build without setup
The repo's README claims WebView2Loader.dll lives under vendor/webview2/,
but only the headers and devcon.exe were tracked. The DLL+import lib for
both x64 and arm64 sat in developers' working trees and were blocked
from git by the over-broad Visual Studio .gitignore rules x64/ and
[Aa][Rr][Mm]64/ (which match any directory of that name at any depth).

Fresh clones therefore hit MSB3073 (xcopy exit 4) at the AppSandbox
post-build step because the source DLL was missing.

Microsoft's WebView2 SDK license permits redistribution of
WebView2Loader.dll; the files are 150 KB each, stable, and required at
runtime. Added negation rules in .gitignore so future vendor binaries
in those paths don't get re-blocked.
2026-05-17 10:55:47 -04:00
James Stringer 25b1b48e5b Allow building from a path that contains spaces
Every PostBuildEvent xcopy command ended a quoted path with a trailing
backslash (e.g. "$(OutDir)resources\"). cmd.exe / CommandLineToArgvW
interpret the \" as an escaped literal quote, so xcopy receives a path
with an embedded " and an unclosed quote state - works fine when no
quotes are needed (no spaces in the path) but fails the moment the
parent path contains a space.

Drop the trailing backslash on each destination; xcopy /I infers
"directory" without it. For the bare "$(OutDir)" case, append a . so
the closing quote isn't preceded by a backslash.
2026-05-17 10:48:19 -04:00
James Stringer afab17b9e0 Auto-pick NAT subnet at runtime to avoid host-adapter overlap
Default Switch ships on every Windows 11 at 172.20.48.0/20, which our
hardcoded 172.20.0.0/16 superseded. HCN rejected the create with
ERROR_DUP_NAME (Microsoft re-uses that error code for subnet conflicts;
the user-facing message is misleading).

New behavior: on first NAT VM start of a process, enumerate every IPv4
subnet bound to a host adapter and pick the first 192.168.N.0/24 with
N>=42 that does not overlap any of them. Cached for process lifetime.

Consumers (allocate_nat_ip, set_ip agent command, endpoint create) now
read the picked prefix/gateway/base via hcn_nat_* accessors instead of
hardcoded 172.20 literals.
2026-05-17 10:33:12 -04:00
James Stringer 5b5c111105 Auto-select default adapter when switching VM to bridged 2026-05-05 19:32:40 -04:00
James Stringer 15bbf36f36 Retry next NAT IP when endpoint creation fails 2026-05-05 19:32:37 -04:00
James Stringer 7d76acdf52 Make downloads more reliable on macOS 2026-04-22 22:43:37 -04:00
James Stringer 228a3095a4 Fix unable to interrupt download 2026-04-22 21:48:16 -04:00
James Stringer f7a6878380 Add a new try all GPU mode (hcs gpu mirror) 2026-04-21 18:31:16 -04:00
James Stringer 7e4fac0d41 make the audio buffers same size on client/host 2026-04-20 21:56:14 -04:00
James Stringer 5bfe945c80 improve windows clipboard 2026-04-20 21:19:56 -04:00
James Stringer aaff11af8f Merge branch 'main' of https://github.com/jamesstringer90/appsandbox 2026-04-19 20:45:44 -04:00
James Stringer 048225fff2 Update README.md 2026-04-19 20:45:36 -04:00
James Stringer 7861ef80ca Remove redundant text from macOS requirements 2026-04-19 20:42:51 -04:00
James Stringer 32fac61cf6 Add macOS details 2026-04-19 20:41:55 -04:00
James Stringer 5dea4a5817 Add macOS support for running macOS VMs
Mac Support
2026-04-19 20:11:26 -04:00
James Stringer 97c0d4c17d don't sync clipboard unless vm window open + mute vm on window close 2026-04-19 19:49:42 -04:00
James Stringer e642f97e75 Improve clipboard, don't sleep displays 2026-04-19 17:32:53 -04:00
James Stringer 8902aefbab handle shutdown better 2026-04-18 23:38:47 -04:00
James Stringer ad551fb39e Set Computer Name 2026-04-18 22:59:38 -04:00
James Stringer aad2902c0a SSH + Clipboard + File staging for macOS 2026-04-18 22:14:59 -04:00
James Stringer 4c458048c8 don't auto start for now 2026-04-17 20:51:34 -04:00
James Stringer 79dd88d85c Update vz_install.m 2026-04-17 20:28:59 -04:00
James Stringer 19e0425d3a fix attempt number 1 billion 2026-04-17 20:26:21 -04:00
James Stringer 44e9b616db fix auto start after install 2026-04-17 20:16:33 -04:00
James Stringer f121d26b24 Update vz_install.m 2026-04-17 20:06:51 -04:00
James Stringer f716f68a39 log improvements 2026-04-17 19:59:40 -04:00
James Stringer fadcbd29d3 fixes 2026-04-17 19:43:06 -04:00
James Stringer adf98fad45 Update ui.m 2026-04-17 18:43:07 -04:00
James Stringer 43ab329bc2 oops 2026-04-17 18:26:31 -04:00
James Stringer 3fdef5d4b7 Update ui.m 2026-04-17 18:16:01 -04:00
James Stringer 9bf18f5311 more refactor 2026-04-17 18:12:44 -04:00
James Stringer 3d51107f88 refactor 2026-04-17 18:06:39 -04:00
James Stringer 54c04e50fb Show host GPU name in VM table on macOS
The GPU attached to a macOS VM is implicit (shared with the host via
paravirtualized graphics), so populate every VM row's gpuName with the
host's Metal default device name. Silences the module-map warning from
DEFINES_MODULE and links Metal.framework into AppSandboxCore.
2026-04-16 19:41:00 -04:00
James Stringer 6e7ecbc218 Redirect Xcode build output to bin/ alongside src/ 2026-04-16 19:18:51 -04:00
James Stringer 1665a58c7b test 2026-04-16 19:13:19 -04:00
James Stringer ac7f58c4d3 spit mac app into front end and dylib 2026-04-16 19:02:28 -04:00
James Stringer 09631de935 Hide snapshot column in VM table on macOS 2026-04-16 18:31:05 -04:00
James Stringer 7ecf9aa375 Reorganize into symmetric app_win/app_mac/backend_win/backend_mac/core layout
Move Windows app shell (main.c, ui.c, webview2_bridge) into src/app_win
and macOS app shell into src/app_mac. Move Xcode project to repo root
alongside the Visual Studio .sln. Both platforms now follow the same
structure:

  src/app_win/        Windows app shell
  src/app_mac/        macOS app shell
  src/backend_win/    Windows HCS engine
  src/backend_mac/    macOS VZ engine
  src/core/           shared types and backend vtable
2026-04-16 17:45:34 -04:00
James Stringer 5f2f3f9327 Auto open/close display window on VM state transitions
Display window opens automatically when VM transitions to
Running and closes when it transitions to Stopped.
2026-04-14 23:20:39 -04:00
James Stringer 18eb292f1f Fix Xcode warnings: icon sizes, deprecation, nullability
- Asset catalog now only assigns the 1024x1024 PNG to the
  512x512@2x slot, leaving other sizes empty
- Replace deprecated allowedFileTypes with allowedContentTypes
- Add NS_ASSUME_NONNULL_BEGIN/END to all backend_mac headers
2026-04-14 23:17:40 -04:00