From 5126a16ffde92a4669cac43834b9f49e8a1b4982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Tue, 17 Mar 2026 13:08:45 +0000 Subject: [PATCH] Add USB systemd services: attach/detach templates, persistent attach, cleanup Co-Authored-By: Claude Opus 4.6 --- modules/services.nix | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/modules/services.nix b/modules/services.nix index 9b86985..d1e5d6e 100644 --- a/modules/services.nix +++ b/modules/services.nix @@ -131,8 +131,13 @@ in cleanupSocket = pkgs.writeShellScript "cleanup-socket-${vm.name}" '' rm -f /run/vmsilo/${vm.name}-crosvm-control.socket ''; + usbCleanup = pkgs.writeShellScript "usb-cleanup-${vm.name}" '' + source ${cfg._internal.usbHelperLib} + usb_cleanup_vm "${vm.name}" + ''; stopPostScripts = [ "${cleanupSocket}" + "${usbCleanup}" ] ++ lib.optionals (vm.rootOverlay.type == "raw") [ "${deleteEphemeral}" ]; in @@ -295,6 +300,81 @@ in }; } ) vms + ++ [ + # USB attach/detach oneshot template services (invoked by vmsilo-usb CLI) + (lib.nameValuePair "vmsilo-usb-attach@" { + description = "vmsilo USB attach: %I"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.writeShellScript "vmsilo-usb-do-attach" '' + source ${cfg._internal.usbHelperLib} + IFS=':' read -r vm_name devpath dev_file vid pid serial busnum devnum <<< "$1" + usb_lock + usb_do_attach "$vm_name" "$devpath" "$dev_file" "$vid" "$pid" "$serial" "$busnum" "$devnum" + usb_unlock + ''} %I"; + }; + }) + (lib.nameValuePair "vmsilo-usb-detach@" { + description = "vmsilo USB detach: %I"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.writeShellScript "vmsilo-usb-do-detach" '' + source ${cfg._internal.usbHelperLib} + IFS=':' read -r vm_name devpath <<< "$1" + usb_lock + usb_do_detach "$vm_name" "$devpath" + usb_unlock + ''} %I"; + }; + }) + ] + ++ + # Persistent USB attach services (one per VM with usbDevices) + lib.concatMap ( + vm: + lib.optional (vm.usbDevices != []) ( + lib.nameValuePair "vmsilo-${vm.name}-usb-attach" { + description = "USB device attach for VM ${vm.name}"; + requires = [ "vmsilo-${vm.name}-vm.service" ]; + after = [ "vmsilo-${vm.name}-vm.service" ]; + wantedBy = [ "vmsilo-${vm.name}-vm.service" ]; + bindsTo = [ "vmsilo-${vm.name}-vm.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = pkgs.writeShellScript "vmsilo-usb-attach-${vm.name}" '' + source ${cfg._internal.usbHelperLib} + SOCKET="/run/vmsilo/${vm.name}-crosvm-control.socket" + # Wait for control socket (up to 30s) + ELAPSED=0 + while [ ! -S "''${SOCKET}" ] && [ "''${ELAPSED}" -lt 30 ]; do + sleep 0.5 + ELAPSED=$((ELAPSED + 1)) + done + if [ ! -S "''${SOCKET}" ]; then + echo "Timeout waiting for control socket: ''${SOCKET}" >&2 + exit 1 + fi + usb_lock + ${lib.concatMapStringsSep "\n" (dev: '' + devices=$(usb_find_by_vidpid "${dev.vendorId}" "${dev.productId}" "${toString (dev.serial or "")}") + count=$(echo "''${devices}" | ${pkgs.jq}/bin/jq 'length') + if [ "''${count}" -eq 0 ]; then + echo "Warning: USB device ${dev.vendorId}:${dev.productId} not found" >&2 + else + echo "''${devices}" | ${pkgs.jq}/bin/jq -r '.[] | [.devpath, .dev_file, .vid, .pid, .serial, .busnum, .devnum] | @tsv' | \ + while IFS=$'\t' read -r devpath dev_file vid pid serial busnum devnum; do + usb_do_attach "${vm.name}" "''${devpath}" "''${dev_file}" "''${vid}" "''${pid}" "''${serial}" "''${busnum}" "''${devnum}" || true + done + fi + '') vm.usbDevices} + usb_unlock + ''; + }; + } + ) + ) vms ); # Session-bind user services: tie GPU VM lifecycle to the desktop session