From 38a96b79b36de600ec7ba2472a8b6e878dd4a29d Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 6 Mar 2026 04:53:30 -0300 Subject: [PATCH] [BREAKING] Provide runtime environment systemd services from munix These services evolve as munix evolves, so they should not be part of the system closures themselves. Mount them into /run/systemd instead. (Yes, making /run/systemd/system a symlink to RO files is unfortunate, that could be changed in the future. FS prep code is annoying too..) --- devShells/default.nix | 2 + flake.nix | 2 + micro-activate.rs | 2 + munix | 10 ++ nixosModules/default.nix | 109 ------------------ packages/munix/default.nix | 15 ++- .../muvm-configure-network.service | 1 + .../microvm.target.wants/muvm-remote.service | 1 + .../pipewire-bridge.socket | 1 + .../session-bus-bridge.service | 1 + .../wayland-bridge.socket | 1 + systemd/muvm-configure-network.service | 6 + systemd/muvm-remote.service | 33 ++++++ systemd/pipewire-bridge.service | 12 ++ systemd/pipewire-bridge.socket | 8 ++ systemd/session-bus-bridge.service | 11 ++ systemd/wayland-bridge.service | 13 +++ systemd/wayland-bridge.socket | 9 ++ 18 files changed, 125 insertions(+), 112 deletions(-) create mode 100644 systemd/microvm.target.wants/muvm-configure-network.service create mode 100644 systemd/microvm.target.wants/muvm-remote.service create mode 100644 systemd/microvm.target.wants/pipewire-bridge.socket create mode 100644 systemd/microvm.target.wants/session-bus-bridge.service create mode 100644 systemd/microvm.target.wants/wayland-bridge.socket create mode 100644 systemd/muvm-configure-network.service create mode 100644 systemd/muvm-remote.service create mode 100644 systemd/pipewire-bridge.service create mode 100644 systemd/pipewire-bridge.socket create mode 100644 systemd/session-bus-bridge.service create mode 100644 systemd/wayland-bridge.service create mode 100644 systemd/wayland-bridge.socket diff --git a/devShells/default.nix b/devShells/default.nix index 382eb7b..0798e86 100644 --- a/devShells/default.nix +++ b/devShells/default.nix @@ -10,6 +10,7 @@ libkrun, muvm, sidebus-broker, + wl-cross-domain-proxy, pkgs, }: @@ -32,6 +33,7 @@ mkShell { passt bubblewrap sidebus-broker + wl-cross-domain-proxy ] ++ (with pkgs; [ meson diff --git a/flake.nix b/flake.nix index e7904a2..cbc0887 100644 --- a/flake.nix +++ b/flake.nix @@ -121,6 +121,7 @@ munix = pkgs.callPackage ./packages/munix { mesa = self'.packages.mesa; muvm = self'.packages.muvm; + wl-cross-domain-proxy = self'.packages.wl-cross-domain-proxy; sidebus-broker = sidebus.packages.${system}.sidebus-broker; }; @@ -130,6 +131,7 @@ devShells.default = pkgs.callPackage ./devShells { libkrun = self'.packages.libkrun; muvm = self'.packages.muvm; + wl-cross-domain-proxy = self'.packages.wl-cross-domain-proxy; sidebus-broker = sidebus.packages.${system}.sidebus-broker; }; }; diff --git a/micro-activate.rs b/micro-activate.rs index 776dae4..765fb30 100644 --- a/micro-activate.rs +++ b/micro-activate.rs @@ -90,6 +90,8 @@ fn main() -> Result<(), std::io::Error> { std::fs::write("/run/localtime", &localtime)?; std::fs::write("/run/resolv.conf", &resolv_conf)?; std::fs::write("/run/machine-id", &gen_machine_id())?; + std::fs::create_dir("/run/systemd")?; + std::os::unix::fs::symlink("/opt/systemd", "/run/systemd/system")?; std::os::unix::fs::symlink(&closure, "/run/current-system")?; if let Ok(tmp_graphics) = std::fs::read(format!("{closure}/etc/tmpfiles.d/graphics-driver.conf")) diff --git a/munix b/munix index ecd7920..7fbd9b0 100755 --- a/munix +++ b/munix @@ -2,8 +2,10 @@ SCRIPT_PATH=$(dirname $(realpath -s $0)) MUVM_PATH=$(dirname $(which muvm)) PASST_PATH=$(dirname $(which passt)) +WL_PROXY_PATH=$(dirname $(which wl-cross-domain-proxy)) HOST_OPENGL_DRIVER=/run/opengl-driver : "${MICROVM_DEFAULT_COMMAND:=bash}" +: "${MUNIX_SYSTEMD_UNITS:="${SCRIPT_PATH}/systemd"}" MICROVM_CLOSURE= MICROVM_COMMAND=() MICROVM_UID=1337 @@ -41,6 +43,7 @@ while [ "$#" -gt 0 ]; do --munix-bin-dir) SCRIPT_PATH="$2"; shift 2;; --muvm-bin-dir) MUVM_PATH="$2"; shift 2;; --passt-bin-dir) PASST_PATH="$2"; shift 2;; + --wl-proxy-bin-dir) WL_PROXY_PATH="$2"; shift 2;; --) shift 1; MICROVM_COMMAND+=("$@"); break;; -*) echo "munix: unknown option: $1" >&2; exit 1;; *) @@ -68,6 +71,11 @@ if [ "$PASST_PATH" = "" ]; then exit 1 fi +if [ "$WL_PROXY_PATH" = "" ]; then + echo "munix: wl-cross-domain-proxy not found, provide a --wl-proxy-bin-dir or fix \$PATH" >&2 + exit 1 +fi + if [ ! -e "$HOST_OPENGL_DRIVER" ]; then echo "munix: host graphics driver not found, provide a --host-opengl-driver" >&2 exit 1 @@ -227,10 +235,12 @@ bwrap --unshare-all --share-net \ --ro-bind "$MUVM_PATH" /run/munix/muvm \ --ro-bind "$PASST_PATH" /run/munix/passt \ --ro-bind "$SCRIPT_PATH/micro-activate" /opt/bin/micro-activate \ + --ro-bind "$WL_PROXY_PATH/wl-cross-domain-proxy" /opt/bin/wl-cross-domain-proxy \ --ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-remote \ --ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-configure-network \ --ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-pwbridge \ --ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-dbusbridge \ + --ro-bind "$MUNIX_SYSTEMD_UNITS" /opt/systemd \ --symlink "$MICROVM_CLOSURE/etc" /etc \ --symlink "$MICROVM_CLOSURE/sw/bin/sh" /bin/sh \ --symlink "$MICROVM_CLOSURE/sw/bin/env" /usr/bin/env \ diff --git a/nixosModules/default.nix b/nixosModules/default.nix index e670144..35398b8 100644 --- a/nixosModules/default.nix +++ b/nixosModules/default.nix @@ -9,12 +9,6 @@ ... }: let - useTTY = { - TTYPath = "/dev/hvc0"; - StandardOutput = "tty"; - StandardInput = "tty"; - StandardError = "tty"; - }; runtimeDir = "/run/vm-user"; system = pkgs.stdenv.hostPlatform.system; in @@ -171,95 +165,6 @@ in systemd.settings.Manager.DefaultEnvironment = "XDG_RUNTIME_DIR=${runtimeDir}"; - systemd.services.muvm-remote = { - enable = true; - description = "microVM Application runner"; - onFailure = [ "exit.target" ]; - onSuccess = [ "exit.target" ]; - wants = [ "sockets.target" ]; - after = [ "sockets.target" ]; - wantedBy = [ "microvm.target" ]; - serviceConfig = { - Type = "exec"; - PassEnvironment = [ - "MESA_LOADER_DRIVER_OVERRIDE" - "MUVM_REMOTE_CONFIG" - # "KRUN_CONFIG" - - "TERM" - "XDG_SESSION_TYPE" - "SDL_VIDEODRIVER" - "QT_QPA_PLATFORM" - "_JAVA_AWT_WM_NONREPARENTING" - "ELECTRON_OZONE_PLATFORM_HINT" - "GTK_USE_PORTAL" - "QT_QPA_PLATFORMTHEME" - ]; - Environment = [ - "WAYLAND_DISPLAY=wayland-1" - "DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeDir}/dbus.sock" - "PATH=/run/current-system/sw/bin" - ]; - User = "appvm"; - Group = "appvm"; - ExecStartPre = "+/run/current-system/sw/bin/chown appvm:appvm ${runtimeDir}"; - ExecStart = "/opt/bin/muvm-remote"; - ExecStopPost = ''+${pkgs.python3}/bin/python -c "import os,fcntl,struct;print(os.getenv('EXIT_STATUS', '1'));fcntl.ioctl(os.open('/', os.O_RDONLY), 0x7602, int(os.getenv('EXIT_STATUS', '1')))"''; - } - // useTTY; - }; - - systemd.services.muvm-configure-network = { - enable = true; - description = "microVM Network configuration"; - wantedBy = [ "microvm.target" ]; - serviceConfig.Type = "oneshot"; - serviceConfig.ExecStart = "/opt/bin/muvm-configure-network"; - }; - - systemd.sockets.muvm-pwbridge = { - enable = true; - description = "PipeWire cross-domain proxy socket"; - wantedBy = [ "microvm.target" ]; - partOf = [ "muvm-pwbridge.service" ]; - listenStreams = [ "${runtimeDir}/pipewire-0" ]; - socketConfig = { - SocketUser = "appvm"; - SocketGroup = "appvm"; - }; - }; - systemd.services.muvm-pwbridge = { - enable = true; - description = "PipeWire cross-domain proxy"; - requires = [ "muvm-pwbridge.socket" ]; - serviceConfig.Type = "exec"; - serviceConfig.ExecStart = "/opt/bin/muvm-pwbridge"; - }; - - systemd.sockets.wayland-proxy = { - enable = true; - description = "Wayland cross-domain proxy socket"; - wantedBy = [ "microvm.target" ]; - partOf = [ "wayland-proxy.service" ]; - listenStreams = [ "${runtimeDir}/wayland-1" ]; - socketConfig = { - SocketUser = "appvm"; - SocketGroup = "appvm"; - FileDescriptorName = "wayland"; - }; - }; - systemd.services.wayland-proxy = { - enable = true; - description = "Wayland cross-domain proxy"; - requires = [ "wayland-proxy.socket" ]; - serviceConfig = { - ExecStartPre = "+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128"; - ExecStart = "${self.packages.${system}.wl-cross-domain-proxy}/bin/wl-cross-domain-proxy --listen-fd --filter-global wp_presentation"; - User = "appvm"; - Group = "appvm"; - }; - }; - systemd.sockets.session-bus = { enable = true; description = "D-Bus session bus socket"; @@ -281,20 +186,6 @@ in Group = "appvm"; }; }; - systemd.services.session-bus-bridge = { - enable = true; - description = "D-Bus session bus"; - wantedBy = ["microvm.target"]; - requires = ["session-bus.socket" "session-bus.service"]; - after = ["session-bus.service"]; - serviceConfig = { - Environment = ["DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeDir}/dbus.sock"]; - ExecStartPre = "+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128"; - ExecStart = "/opt/bin/muvm-dbusbridge"; - User = "appvm"; - Group = "appvm"; - }; - }; hardware.graphics.enable = true; hardware.graphics.package = self.packages.${system}.mesa; diff --git a/packages/munix/default.nix b/packages/munix/default.nix index ed82b9a..d7ff431 100644 --- a/packages/munix/default.nix +++ b/packages/munix/default.nix @@ -1,9 +1,18 @@ -{ stdenv, writeScriptBin, symlinkJoin, makeWrapper, muvm, passt, bubblewrap, sidebus-broker, mesa, rustc }: +{ stdenv, writeScriptBin, symlinkJoin, makeWrapper, muvm, passt, bubblewrap, sidebus-broker, wl-cross-domain-proxy, mesa, rustc }: let munixScript = (writeScriptBin "munix" (builtins.readFile ../../munix)).overrideAttrs(old: { buildCommand = "${old.buildCommand}\n patchShebangs $out"; }); + munixSystemd = stdenv.mkDerivation { + name = "munix-systemd"; + src = ../../systemd; + dontUnpack = true; + installPhase = '' + mkdir -p $out + cp -aR $src/* $out + ''; + }; microActivate = stdenv.mkDerivation { name = "micro-activate"; src = ../../micro-activate.rs; @@ -19,9 +28,9 @@ let }; in symlinkJoin { name = "munix"; - paths = [ munixScript microActivate muvm passt bubblewrap sidebus-broker ]; + paths = [ munixScript microActivate muvm passt bubblewrap sidebus-broker wl-cross-domain-proxy ]; buildInputs = [ makeWrapper ]; postBuild = '' - wrapProgram $out/bin/munix --prefix PATH : $out/bin --set FALLBACK_OPENGL_DRIVER ${mesa} + wrapProgram $out/bin/munix --prefix PATH : $out/bin --set FALLBACK_OPENGL_DRIVER ${mesa} --set MUNIX_SYSTEMD_UNITS ${munixSystemd} ''; } diff --git a/systemd/microvm.target.wants/muvm-configure-network.service b/systemd/microvm.target.wants/muvm-configure-network.service new file mode 100644 index 0000000..ca93c89 --- /dev/null +++ b/systemd/microvm.target.wants/muvm-configure-network.service @@ -0,0 +1 @@ +../muvm-configure-network.service \ No newline at end of file diff --git a/systemd/microvm.target.wants/muvm-remote.service b/systemd/microvm.target.wants/muvm-remote.service new file mode 100644 index 0000000..4f9f550 --- /dev/null +++ b/systemd/microvm.target.wants/muvm-remote.service @@ -0,0 +1 @@ +../muvm-remote.service \ No newline at end of file diff --git a/systemd/microvm.target.wants/pipewire-bridge.socket b/systemd/microvm.target.wants/pipewire-bridge.socket new file mode 100644 index 0000000..95e1850 --- /dev/null +++ b/systemd/microvm.target.wants/pipewire-bridge.socket @@ -0,0 +1 @@ +../pipewire-bridge.socket \ No newline at end of file diff --git a/systemd/microvm.target.wants/session-bus-bridge.service b/systemd/microvm.target.wants/session-bus-bridge.service new file mode 100644 index 0000000..faa559d --- /dev/null +++ b/systemd/microvm.target.wants/session-bus-bridge.service @@ -0,0 +1 @@ +../session-bus-bridge.service \ No newline at end of file diff --git a/systemd/microvm.target.wants/wayland-bridge.socket b/systemd/microvm.target.wants/wayland-bridge.socket new file mode 100644 index 0000000..4bcb084 --- /dev/null +++ b/systemd/microvm.target.wants/wayland-bridge.socket @@ -0,0 +1 @@ +../wayland-bridge.socket \ No newline at end of file diff --git a/systemd/muvm-configure-network.service b/systemd/muvm-configure-network.service new file mode 100644 index 0000000..3a67ff4 --- /dev/null +++ b/systemd/muvm-configure-network.service @@ -0,0 +1,6 @@ +[Unit] +Description=microVM Network configuration + +[Service] +Type=oneshot +ExecStart=/opt/bin/muvm-configure-network diff --git a/systemd/muvm-remote.service b/systemd/muvm-remote.service new file mode 100644 index 0000000..6164434 --- /dev/null +++ b/systemd/muvm-remote.service @@ -0,0 +1,33 @@ +[Unit] +After=sockets.target +Description=microVM Application runner +OnFailure=exit.target +OnSuccess=exit.target +Wants=sockets.target + +[Service] +Type=exec +# Environment="LOCALE_ARCHIVE=/nix/store/1hilqf0v1nm2w8xj87pwpm0rgq6lvhlv-glibc-locales-2.42-47/lib/locale/locale-archive" +# Environment="TZDIR=/nix/store/80izpiglrlqv2zb7rd7m5274k2dr142l-tzdata-2025c/share/zoneinfo" +Environment=WAYLAND_DISPLAY=wayland-1 +Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/vm-user/dbus.sock +Environment=PATH=/run/current-system/sw/bin +PassEnvironment=MESA_LOADER_DRIVER_OVERRIDE +PassEnvironment=MUVM_REMOTE_CONFIG +PassEnvironment=TERM +PassEnvironment=XDG_SESSION_TYPE +PassEnvironment=SDL_VIDEODRIVER +PassEnvironment=QT_QPA_PLATFORM +PassEnvironment=_JAVA_AWT_WM_NONREPARENTING +PassEnvironment=ELECTRON_OZONE_PLATFORM_HINT +PassEnvironment=GTK_USE_PORTAL +PassEnvironment=QT_QPA_PLATFORMTHEME +ExecStartPre=+/run/current-system/sw/bin/chown appvm:appvm /run/vm-user +ExecStart=/opt/bin/muvm-remote +ExecStopPost=+/nix/store/ygzfhw5nxrn7qqfqmz2s9p6cikcjqqkh-python3-3.13.11/bin/python -c "import os,fcntl,struct;print(os.getenv('EXIT_STATUS', '1'));fcntl.ioctl(os.open('/', os.O_RDONLY), 0x7602, int(os.getenv('EXIT_STATUS', '1')))" +User=appvm +Group=appvm +StandardError=tty +StandardInput=tty +StandardOutput=tty +TTYPath=/dev/hvc0 diff --git a/systemd/pipewire-bridge.service b/systemd/pipewire-bridge.service new file mode 100644 index 0000000..25c53e2 --- /dev/null +++ b/systemd/pipewire-bridge.service @@ -0,0 +1,12 @@ +[Unit] +Description=PipeWire VM bridge +Requires=pipewire-bridge.socket + +[Service] +Type=exec +ExecStart=/opt/bin/muvm-pwbridge + +# Environment=RUST_LOG=debug +# StandardError=tty +# StandardOutput=tty +# TTYPath=/dev/hvc0 diff --git a/systemd/pipewire-bridge.socket b/systemd/pipewire-bridge.socket new file mode 100644 index 0000000..998e661 --- /dev/null +++ b/systemd/pipewire-bridge.socket @@ -0,0 +1,8 @@ +[Unit] +Description=PipeWire VM bridge socket +PartOf=pipewire-bridge.service + +[Socket] +SocketGroup=appvm +SocketUser=appvm +ListenStream=/run/vm-user/pipewire-0 diff --git a/systemd/session-bus-bridge.service b/systemd/session-bus-bridge.service new file mode 100644 index 0000000..9b12a85 --- /dev/null +++ b/systemd/session-bus-bridge.service @@ -0,0 +1,11 @@ +[Unit] +After=session-bus.service +Description=D-Bus VM integration bridge +Requires=session-bus.socket session-bus.service + +[Service] +Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/vm-user/dbus.sock +ExecStartPre=+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128 +ExecStart=/opt/bin/muvm-dbusbridge +Group=appvm +User=appvm diff --git a/systemd/wayland-bridge.service b/systemd/wayland-bridge.service new file mode 100644 index 0000000..c74ba4e --- /dev/null +++ b/systemd/wayland-bridge.service @@ -0,0 +1,13 @@ +[Unit] +Description=Wayland VM bridge +Requires=wayland-bridge.socket + +[Service] +ExecStartPre=+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128 +ExecStart=/opt/bin/wl-cross-domain-proxy --listen-fd --filter-global wp_presentation +User=appvm +Group=appvm + +StandardError=tty +StandardOutput=tty +TTYPath=/dev/hvc0 diff --git a/systemd/wayland-bridge.socket b/systemd/wayland-bridge.socket new file mode 100644 index 0000000..2706a2b --- /dev/null +++ b/systemd/wayland-bridge.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Wayland VM bridge socket +PartOf=wayland-bridge.service + +[Socket] +FileDescriptorName=wayland +SocketGroup=appvm +SocketUser=appvm +ListenStream=/run/vm-user/wayland-1