From 6dcd2a370a8065ef6e4dbc6e7a0b2dbfd4b40d47 Mon Sep 17 00:00:00 2001 From: "Else, Someone" Date: Thu, 9 Oct 2025 20:55:48 +0300 Subject: [PATCH 1/4] vmapp-demo: copypaste-ability --- profiles/vmapp-demo.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/profiles/vmapp-demo.nix b/profiles/vmapp-demo.nix index 67078aa..2824b46 100644 --- a/profiles/vmapp-demo.nix +++ b/profiles/vmapp-demo.nix @@ -8,6 +8,7 @@ }: let + inherit (config._module.args) inputs; cfg = config.vmapps; desktopItems' = pkgs.callPackage ../pkgs/desktopAdapters.nix { }; desktopItems = desktopItems'.overrideScope ( @@ -28,8 +29,8 @@ let modules = extraModules ++ [ { microvm.hypervisor = "cloud-hypervisor"; - microvm.hotpluggedMem = 128; - microvm.hotplugMem = 512; + microvm.hotpluggedMem = lib.mkDefault 128; + microvm.hotplugMem = lib.mkDefault 512; microvm.shares = [ { source = "send"; @@ -50,10 +51,10 @@ let ./uvms-guest.nix ./vsock-connect-guest.nix (modulesPath + "/profiles/minimal.nix") - (config._module.args.inputs."microvm.nix" + "/nixos-modules/microvm") + (inputs."microvm.nix" + "/nixos-modules/microvm") ]; specialArgs = { - inherit (config._module.args) inputs; + inherit inputs; }; }; runner = evaluated.config.microvm.declaredRunner; From dc583da67e94df4e576504436aef875a9dbf8e12 Mon Sep 17 00:00:00 2001 From: "Else, Someone" Date: Sun, 26 Oct 2025 20:49:14 +0200 Subject: [PATCH 2/4] runInVM: split out ...the ${vmName} argument was kept for compat, which is no longer necessary --- pkgs/desktopAdapters.nix | 52 ++++++++++++++++++++++------------------ profiles/vmapp-demo.nix | 2 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/pkgs/desktopAdapters.nix b/pkgs/desktopAdapters.nix index bb5a7fb..23c9eb3 100644 --- a/pkgs/desktopAdapters.nix +++ b/pkgs/desktopAdapters.nix @@ -2,6 +2,7 @@ lib, newScope, makeDesktopItem, + writeShellScript, writeShellScriptBin, }: @@ -18,29 +19,34 @@ lib.makeScope newScope (self: { mkRunInVM = vmName: lib.getExe (self.mkRunInVMPkg vmName); mkRunInVMPkg = vmName: - writeShellScriptBin "run-in-${vmName}" '' - localArgs=( - ${lib.optionalString self.useSessionUnits "--user"} - --property=Requires=${self.vmUnitPrefix}${vmName}.service - --property=After=${self.vmUnitPrefix}${vmName}.service - --property=SyslogIdentifier="$1 (${vmName})" - ) - remoteArgs=( - systemd-run - --user - --property=StandardError="journal+console" - --property=StandardOutput="journal+console" - --property=OOMScoreAdjust=${toString self.oomScoreAdjust} - ${lib.optionalString (self.waylandProxyUnit != null) '' - --property=Requires=${self.waylandProxyUnit} - --property=After=${self.waylandProxyUnit} - ''} - ) - systemd-run \ - "''${localArgs[@]}" \ - ssh ${self.vmUser}@${self.vmSshPrefix}${vmName} \ - "''${remoteArgs[@]}" $@ - ''; + lib.warn "Use `\${runInVM} foobar` instead of `\${mkRunInVMPkg foobar}`" ( + writeShellScriptBin "run-in-${vmName}" "${lib.getExe self.runInVM} ${vmName} $@" + ); + runInVM = writeShellScriptBin "run-in" '' + vmName=$1 + shift + localArgs=( + ${lib.optionalString self.useSessionUnits "--user"} + --property=Requires="${self.vmUnitPrefix}$vmName".service + --property=After="${self.vmUnitPrefix}$vmName".service + --property=SyslogIdentifier="$1 ($vmName)" + ) + remoteArgs=( + systemd-run + --user + --property=StandardError="journal+console" + --property=StandardOutput="journal+console" + --property=OOMScoreAdjust=${toString self.oomScoreAdjust} + ${lib.optionalString (self.waylandProxyUnit != null) '' + --property=Requires=${self.waylandProxyUnit} + --property=After=${self.waylandProxyUnit} + ''} + ) + systemd-run \ + "''${localArgs[@]}" \ + ssh "${self.vmUser}@${self.vmSshPrefix}$vmName" \ + "''${remoteArgs[@]}" $@ + ''; # Actual .desktop item adapters toVM = diff --git a/profiles/vmapp-demo.nix b/profiles/vmapp-demo.nix index 2824b46..2f960b2 100644 --- a/profiles/vmapp-demo.nix +++ b/profiles/vmapp-demo.nix @@ -240,7 +240,7 @@ in [ { environment.systemPackages = [ - (desktopItems.mkRunInVMPkg "browser") + (desktopItems.runInVM) (pkgs.makeDesktopItem ( desktopItems.toBrowser ( (desktopItems.toVM "browser" { From 927db90791ca2fe83fddb62da7fee473875b33c0 Mon Sep 17 00:00:00 2001 From: "Else, Someone" Date: Fri, 7 Nov 2025 19:02:42 +0200 Subject: [PATCH 3/4] request-usb: init; ugly helper for attaching usb to crosvm --- pkgs/request-usb/package.nix | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkgs/request-usb/package.nix diff --git a/pkgs/request-usb/package.nix b/pkgs/request-usb/package.nix new file mode 100644 index 0000000..69ed284 --- /dev/null +++ b/pkgs/request-usb/package.nix @@ -0,0 +1,63 @@ +{ writeShellScriptBin }: + +writeShellScriptBin "request-usb" '' + set -euo pipefail + + parseArgs() { + N_CONSTRAINTS=0 + while [[ -n "''${1:-}" ]]; do + local x= + case "$1" in + "--manufacturer" ) + N_CONSTRAINTS=$(( $N_CONSTRAINTS + 1 )) + MANUFACTURER="$2" + shift 2 + ;; + "--manufacturer="* ) + N_CONSTRAINTS=$(( $N_CONSTRAINTS + 1 )) + MANUFACTURER=''${1#--manufacturer=} + shift 1 + ;; + "--" ) + break + ;; + *) + printUsage + ;; + esac + done + } + printUsage() { + echo "$(caller)" >&2 + echo "Usage: request-usb [--manufacturer=MANUFACTURER]" >&2 + exit 1 + } + + findFirst() { + local d= + + [[ ''${N_CONSTRAINTS:-0} != 0 ]] || printUsage + for d in /sys/bus/usb/devices/* ; do + [[ -e "$d/manufacturer" ]] || continue + [[ -e "$d/uevent" ]] || continue + + read -r x < "$d/manufacturer" + if ! [[ -z "''${MANUFACTURER:-}" || "$MANUFACTURER" = "$x" ]] ; then + continue + fi + + # NOTE: Other predicates here + # ... + + echo -n "$d" + return + done + return 1 + } + + parseArgs $@ + d=$(findFirst) + export $( cat "$d/uevent" ) + sudo chown "$USER" /dev/bus/usb/"$BUSNUM"/"$DEVNUM" + echo -n /dev/bus/usb/"$BUSNUM"/"$DEVNUM" +'' From 20451db88cdc9d91454e3747f37777ae805d3fda Mon Sep 17 00:00:00 2001 From: "Else, Someone" Date: Fri, 7 Nov 2025 19:04:58 +0200 Subject: [PATCH 4/4] ch-proxy: support normal vsock --- pkgs/ch-proxy/proxy.c | 89 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/pkgs/ch-proxy/proxy.c b/pkgs/ch-proxy/proxy.c index 78bfefd..ed1dea0 100644 --- a/pkgs/ch-proxy/proxy.c +++ b/pkgs/ch-proxy/proxy.c @@ -7,6 +7,8 @@ #include #include +#include + struct msghdr mk_msghdr(); int ch_connect(const char*, const char*); ssize_t send_fd(int, int); @@ -18,7 +20,8 @@ void print_usage() { "Usage:\n" "\tch-proxy uvm/$USER_VM_NAME [PORT]\n" "\tch-proxy uuvm/$VM_NAME [PORT]\n" - "\tch-proxy vsock-mux%$PATH [PORT]\n"); + "\tch-proxy vsock-mux%$PATH [PORT]\n" + "\tch-proxy vsock/cid[:port]\n"); } char *extract_vsock_mux(const char *host_string) { @@ -67,6 +70,50 @@ char *extract_muvm(const char *host_string) { return result; } +int extract_cid(const char *host, int *cid, int *port) { + if (errno != 0) { + perror("extract_cid(...): errno unclean"); + exit(EXIT_FAILURE); + } + + const char PREFIX[] = "vsock/"; + const ssize_t PREFIX_LEN = sizeof(PREFIX) - 1; + + if (strncmp(host, PREFIX, PREFIX_LEN) != 0) { + return 1; + } + + char *in = NULL; + const char *end = host + strlen(host); + const char *sCid = host + PREFIX_LEN; + + long x; + x = strtol(sCid, &in, 10); + if (errno != 0) { + perror("strtol(cid, ...)"); + exit(EXIT_FAILURE); + } + *cid = (int) x; + + if (in == end) { + *port = 22; + return 0; + } else if (in[0] != ':') { + perror("extract_cid(...): expected a string of the form cid[:port]"); + perror(host); + exit(EXIT_FAILURE); + } + + const char *sPort = in + 1; + x = strtol(sPort, &in, 10); + if (errno != 0) { + perror("strtol(port, ...)"); + exit(EXIT_FAILURE); + } + *port = (int) x; + return 0; +} + int main(int argc, char** argv) { if (!(2 <= argc && argc <= 3)) { fprintf(stderr, "%s: Wrong # of arguments: %d\n", argv[0], argc); @@ -79,21 +126,47 @@ int main(int argc, char** argv) { const char *ssh_host = argv[1]; const char *port_string = argc == 3 ? argv[2] : PORT_DEFAULT; - char *path_un; + int cid = -1, port = -1; + char *path_un = NULL; if ((path_un = extract_uvm(ssh_host)) != NULL) { } else if ((path_un = extract_muvm(ssh_host)) != NULL) { } else if ((path_un = extract_vsock_mux(ssh_host)) != NULL) { + } else if (extract_cid(ssh_host, &cid, &port) == 0) { } else { - fprintf(stderr, "ch-proxy/main: unexpected host stirng format: %s\n", ssh_host); + fprintf(stderr, "ch-proxy/main: unexpected host string format: %s\n", ssh_host); print_usage(); return EXIT_FAILURE; } - const int s = ch_connect(path_un, port_string); - if (s == -1) { - perror("ssh-vsock-proxy/main/ch_connect"); - return EXIT_FAILURE; - }; + int s = -1; + + if (path_un != NULL) { + s = ch_connect(path_un, port_string); + if (s == -1) { + perror("ssh-vsock-proxy/main/ch_connect"); + return EXIT_FAILURE; + }; + } else if (cid != -1 && port != -1) { + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_reserved1 = 0, + .svm_port = port, + .svm_cid = cid, + }; + memset(sa.svm_zero, 0, sizeof(sa.svm_zero)); + s = socket(AF_VSOCK, SOCK_STREAM, 0); + if (s == -1) { + perror("socket(AF_VSOCK, ...)"); + exit(EXIT_FAILURE); + } + if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) != 0) { + perror("connect(socket(AF_VSOCK, ...), ...)"); + exit(EXIT_FAILURE); + } + } else { + perror("Couldn't parse neither uuvm/ strings nor vsock/cid[:port]"); + exit(EXIT_FAILURE); + } if (send_fd(1, s) == -1) { perror("ssh-vsock-proxy/main/send_fd");