Skip to content

Commit

Permalink
Add an XDG handler for PDF files to the chromium-vm
Browse files Browse the repository at this point in the history
The XDG handler script is executed by Chromium when the user attempts
to open a PDF file. The script sends the PDF file path to the GUIVM
over a TCP connection. The GUIVM copies the file from chromium-vm to a
temporary location in the zathura-vm and opens it there. Once the viewer
is closed, the file is deleted from the zathura-vm.

Signed-off-by: Yuri Nesterov <[email protected]>
  • Loading branch information
nesteroff authored and brianmcgillion committed Feb 13, 2024
1 parent d4fa6a4 commit 96927a1
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 8 deletions.
20 changes: 19 additions & 1 deletion modules/virtualization/microvm/appvm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@
config,
pkgs,
...
}: {
}: let
waypipeBorder =
if vm.borderColor != null
then "--border \"${vm.borderColor}\""
else "";
runWaypipe = with pkgs;
writeScriptBin "run-waypipe" ''
#!${runtimeShell} -e
${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server $@
'';
in {
ghaf = {
users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable;

Expand Down Expand Up @@ -65,6 +75,7 @@

environment.systemPackages = [
pkgs.waypipe
runWaypipe
];

microvm = {
Expand Down Expand Up @@ -160,6 +171,13 @@ in {
type = int;
default = 0;
};
borderColor = mkOption {
description = ''
Border color of the AppVM window
'';
type = nullOr str;
default = null;
};
};
});
default = [];
Expand Down
92 changes: 85 additions & 7 deletions targets/lenovo-x1-carbon.nix
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@
time.timeZone = "Asia/Dubai";
})
];
guivmConfig = hostConfiguration.config.ghaf.virtualization.microvm.guivm;
winConfig = hostConfiguration.config.ghaf.windows-launcher;
networkDevice = hostConfiguration.config.ghaf.hardware.definition.network.pciDevices;
# TCP port used by PDF XDG handler
xdgPdfPort = 1200;
guivmExtraModules = [
{
# Early KMS needed for GNOME to work inside GuiVM
Expand All @@ -149,29 +150,50 @@
"pa,id=pa1,server=unix:/run/pulse/native"
];
}
({pkgs, ...}: {
({pkgs, ...}: let
sshKeyPath = "/run/waypipe-ssh/id_ed25519";
# The openpdf script is executed by the xdg handler from the chromium-vm
# It reads the file path, copies it from chromium-vm to zathura-vm and opens it there
openPdf = with pkgs;
writeScriptBin "openpdf" ''
#!${runtimeShell} -e
read -r sourcepath
filename=$(basename $sourcepath)
zathurapath="/var/tmp/$filename"
chromiumip=$(${dnsutils}/bin/dig +short chromium-vm.ghaf | head -1)
if [[ "$chromiumip" != "$REMOTE_ADDR" ]]; then
echo "Open PDF request received from $REMOTE_ADDR, but it is only permitted for chromium-vm.ghaf with IP $chromiumip"
exit 0
fi
echo "Copying $sourcepath from $REMOTE_ADDR to $zathurapath in zathura-vm"
${openssh}/bin/scp -i ${sshKeyPath} -o StrictHostKeyChecking=no $REMOTE_ADDR:"$sourcepath" zathura-vm.ghaf:"$zathurapath"
echo "Opening $zathurapath in zathura-vm"
${openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf run-waypipe zathura "$zathurapath"
echo "Deleting $zathurapath in zathura-vm"
${openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf rm -f "$zathurapath"
'';
in {
ghaf.hardware.definition.network.pciDevices = networkDevice;
ghaf.graphics.launchers = let
adwaitaIconsRoot = "${pkgs.gnome.adwaita-icon-theme}/share/icons/Adwaita/32x32/actions/";
hostAddress = "192.168.101.2";
sshKeyPath = "/run/waypipe-ssh/id_ed25519";
powerControl = pkgs.callPackage powerControlPkgPath {};
in [
{
name = "chromium";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no chromium-vm.ghaf ${pkgs.waypipe}/bin/waypipe --border \"#ff5733,5\" --vsock -s ${toString guivmConfig.waypipePort} server chromium --enable-features=UseOzonePlatform --ozone-platform=wayland";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no chromium-vm.ghaf run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland";
icon = "${../assets/icons/png/browser.png}";
}

{
name = "gala";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no gala-vm.ghaf ${pkgs.waypipe}/bin/waypipe --border \"#33ff57,5\" --vsock -s ${toString guivmConfig.waypipePort} server gala --enable-features=UseOzonePlatform --ozone-platform=wayland";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no gala-vm.ghaf run-waypipe gala --enable-features=UseOzonePlatform --ozone-platform=wayland";
icon = "${../assets/icons/png/app.png}";
}

{
name = "zathura";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf ${pkgs.waypipe}/bin/waypipe --border \"#337aff,5\" --vsock -s ${toString guivmConfig.waypipePort} server zathura";
path = "${pkgs.openssh}/bin/ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no zathura-vm.ghaf run-waypipe zathura";
icon = "${../assets/icons/png/pdf.png}";
}

Expand Down Expand Up @@ -213,6 +235,33 @@
];

time.timeZone = "Asia/Dubai";

# PDF XDG handler service receives a PDF file path from the chromium-vm and executes the openpdf script
systemd.user = {
sockets."pdf" = {
unitConfig = {
Description = "PDF socket";
};
socketConfig = {
ListenStream = "${toString xdgPdfPort}";
Accept = "yes";
};
wantedBy = ["sockets.target"];
};

services."pdf@" = {
description = "PDF opener";
serviceConfig = {
ExecStart = "${openPdf}/bin/openpdf";
StandardInput = "socket";
StandardOutput = "journal";
StandardError = "journal";
};
};
};

# Open TCP port for the PDF XDG socket
networking.firewall.allowedTCPPorts = [xdgPdfPort];
})
];
hostConfiguration = lib.nixosSystem {
Expand Down Expand Up @@ -328,7 +377,27 @@
vms = [
{
name = "chromium";
packages = [pkgs.chromium pkgs.pamixer];
packages = let
# PDF XDG handler is executed when the user opens a PDF file in the browser
# The xdgopenpdf script sends a command to the guivm with the file path over TCP connection
xdgPdfItem = pkgs.makeDesktopItem {
name = "ghaf-pdf";
desktopName = "Ghaf PDF handler";
exec = "${xdgOpenPdf}/bin/xdgopenpdf %u";
mimeTypes = ["application/pdf"];
};
xdgOpenPdf = pkgs.writeShellScriptBin "xdgopenpdf" ''
filepath=$(realpath $1)
echo "Opening $filepath" | systemd-cat -p info
echo $filepath | ${pkgs.netcat}/bin/nc -N gui-vm.ghaf ${toString xdgPdfPort}
'';
in [
pkgs.chromium
pkgs.pamixer
pkgs.xdg-utils
xdgPdfItem
xdgOpenPdf
];
macAddress = "02:00:00:03:05:01";
ramMb = 3072;
cores = 4;
Expand Down Expand Up @@ -357,8 +426,15 @@
"hda-duplex,audiodev=pa1"
];
microvm.devices = [];

# Disable chromium built-in PDF viewer to make it execute xdg-open
programs.chromium.enable = true;
programs.chromium.extraOpts."AlwaysOpenPdfExternally" = true;
# Set default PDF XDG handler
xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf.desktop";
}
];
borderColor = "#ff5733";
}
{
name = "gala";
Expand All @@ -371,6 +447,7 @@
time.timeZone = "Asia/Dubai";
}
];
borderColor = "#33ff57";
}
{
name = "zathura";
Expand All @@ -383,6 +460,7 @@
time.timeZone = "Asia/Dubai";
}
];
borderColor = "#337aff";
}
];
};
Expand Down

0 comments on commit 96927a1

Please sign in to comment.