diff options
author | Alyssa Ross <hi@alyssa.is> | 2024-03-02 14:11:36 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2024-03-02 14:11:36 +0100 |
commit | b287ea55210d103ea8798de7c303953027084b10 (patch) | |
tree | a04c3db9cda8232c1af0fd284c77334ce2a9a517 | |
parent | 97dc7de1d4f8486f8538f4e32d5b472001ed4699 (diff) | |
download | spectrum-portals.tar spectrum-portals.tar.gz spectrum-portals.tar.bz2 spectrum-portals.tar.lz spectrum-portals.tar.xz spectrum-portals.tar.zst spectrum-portals.zip |
wip portals
35 files changed, 2181 insertions, 61 deletions
diff --git a/.gitignore b/.gitignore index 0491ebb..8bb5039 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/ result result-* +target/ **/subprojects/* !**/subprojects/*.wrap diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile index c5e467d..a8442a2 100644 --- a/host/rootfs/Makefile +++ b/host/rootfs/Makefile @@ -18,6 +18,7 @@ FILES = \ etc/mdev/wait \ etc/parse-devname \ etc/passwd \ + etc/s6-linux-init/env/NO_AT_BRIDGE \ etc/s6-linux-init/run-image/service/getty-tty1/run \ etc/s6-linux-init/run-image/service/getty-tty2/run \ etc/s6-linux-init/run-image/service/getty-tty3/run \ @@ -35,6 +36,10 @@ FILES = \ etc/s6-linux-init/run-image/service/vmm/notification-fd \ etc/s6-linux-init/run-image/service/vmm/run \ etc/s6-linux-init/run-image/service/vmm/template/notification-fd \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run \ etc/s6-linux-init/scripts/rc.init \ etc/shared-dir \ etc/xdg/weston/autolaunch \ @@ -58,6 +63,10 @@ DIRS = \ etc/s6-linux-init/run-image/service/vmm/instances \ etc/s6-linux-init/run-image/service/vmm/template/data \ etc/s6-linux-init/run-image/service/vmm/template/env \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/instance \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/instances \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/data \ + etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/env \ etc/s6-linux-init/run-image/vm \ ext \ run \ diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix index c6664bd..de2ae5a 100644 --- a/host/rootfs/default.nix +++ b/host/rootfs/default.nix @@ -3,13 +3,15 @@ # SPDX-FileCopyrightText: 2022 Unikie import ../../lib/call-package.nix ( -{ callSpectrumPackage, lseek, src, pkgsMusl, pkgsStatic, linux_latest }: +{ callSpectrumPackage, lseek, src, pkgsMusl, pkgsStatic, linux_latest, xdg-desktop-portal-gtk }: pkgsStatic.callPackage ( { start-vmm , lib, stdenvNoCC, nixos, runCommand, writeReferencesToFile, erofs-utils, s6-rc , busybox, cloud-hypervisor, cryptsetup, execline, e2fsprogs, jq, kmod , mdevd, s6, s6-linux-init, socat, util-linuxMinimal, virtiofsd, xorg +, xdg-desktop-portal-spectrum-host +, strace }: let @@ -19,11 +21,25 @@ let pkgsGui = pkgsMusl.extend ( final: super: (optionalAttrs (systems.equals pkgsMusl.stdenv.hostPlatform super.stdenv.hostPlatform) { + appstream = super.appstream.override { + withSystemd = false; + }; + libgudev = super.libgudev.overrideAttrs ({ ... }: { # Tests use umockdev, which is not compatible with libudev-zero. doCheck = false; }); + polkit = super.polkit.override { + useSystemd = false; + }; + postgresql = super.postgresql.override { + enableSystemd = false; + }; + procps = super.procps.override { + withSystemd = false; + }; + systemd = final.libudev-zero; systemdLibs = final.libudev-zero; systemdMinimal = final.libudev-zero; @@ -32,6 +48,10 @@ let systemdSupport = false; }; + util-linux = super.util-linux.override { + systemdSupport = false; + }; + weston = super.weston.overrideAttrs ({ mesonFlags ? [], ... }: { mesonFlags = mesonFlags ++ [ "-Dsystemd=false" @@ -45,6 +65,8 @@ let packages = [ cloud-hypervisor e2fsprogs execline jq kmod mdevd s6 s6-linux-init s6-rc socat start-vmm virtiofsd + xdg-desktop-portal-spectrum-host + pkgsMusl.strace (cryptsetup.override { programs = { @@ -91,8 +113,10 @@ let packagesSysroot = runCommand "packages-sysroot" { nativeBuildInputs = [ xorg.lndir ]; } '' - mkdir -p $out/usr/bin - ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/usr/bin + mkdir -p $out/usr/bin $out/usr/share + ln -st $out/usr/bin \ + ${concatMapStringsSep " " (p: "${p}/bin/*") packages} \ + ${xdg-desktop-portal-gtk}/libexec/xdg-desktop-portal-gtk for pkg in ${lib.escapeShellArgs usrPackages}; do lndir -ignorelinks -silent "$pkg" "$out/usr" @@ -135,7 +159,7 @@ stdenvNoCC.mkDerivation { enableParallelBuilding = true; - passthru = { inherit appvm firmware kernel nixosAllHardware; }; + passthru = { inherit appvm firmware kernel nixosAllHardware pkgsGui; }; meta = with lib; { license = licenses.eupl12; diff --git a/host/rootfs/etc/s6-linux-init/env/NO_AT_BRIDGE b/host/rootfs/etc/s6-linux-init/env/NO_AT_BRIDGE new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/env/NO_AT_BRIDGE @@ -0,0 +1 @@ +1 diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run b/host/rootfs/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run index c055cc0..a6233b6 100755 --- a/host/rootfs/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run +++ b/host/rootfs/etc/s6-linux-init/run-image/service/vhost-user-fs/template/run @@ -8,5 +8,6 @@ if { fdmove 1 3 echo } fdclose 3 export TMPDIR /run -backtick -E shared_dir { /etc/shared-dir -v servicename=${1} } -virtiofsd --fd 0 --shared-dir $shared_dir +# backtick -E shared_dir { /etc/shared-dir -v servicename=${1} } +define shared_dir /run/vm/${1}/fs +virtiofsd --fd 0 --shared-dir $shared_dir --sandbox none diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd @@ -0,0 +1 @@ +3 diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd.license b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd.license new file mode 100644 index 0000000..a941ca4 --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/notification-fd.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2023 Alyssa Ross <hi@alyssa.is> diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run new file mode 100755 index 0000000..9041788 --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/run @@ -0,0 +1,5 @@ +#!/bin/execlineb -P +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2023 Alyssa Ross <hi@alyssa.is> + +s6-svscan -d3 instance diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd @@ -0,0 +1 @@ +3 diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd.license b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd.license new file mode 100644 index 0000000..5a40633 --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is> diff --git a/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run new file mode 100755 index 0000000..d45fa6d --- /dev/null +++ b/host/rootfs/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run @@ -0,0 +1,16 @@ +#!/bin/execlineb -S1 +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2024 Alyssa Ross <hi@alyssa.is> + +export XDG_RUNTIME_DIR /run/user/0 +export WAYLAND_DISPLAY wayland-1 +export XDG_DESKTOP_PORTAL_SPECTRUM_HOST_FS_ROOT /run/vm/${1}/fs + +if { mkdir -p /run/service/vmm/instance/${1}/env } + +s6-ipcserver-socketbinder -a 0700 /run/service/vmm/instance/${1}/env/vsock.sock_219 + +if { fdmove 1 3 echo } +fdclose 3 + +xdg-desktop-portal-spectrum-host diff --git a/host/rootfs/etc/s6-rc/ext-rc-init/up b/host/rootfs/etc/s6-rc/ext-rc-init/up index 0214759..2532bbe 100644 --- a/host/rootfs/etc/s6-rc/ext-rc-init/up +++ b/host/rootfs/etc/s6-rc/ext-rc-init/up @@ -1,5 +1,5 @@ # SPDX-License-Identifier: EUPL-1.2+ -# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2021-2024 Alyssa Ross <hi@alyssa.is> # SPDX-FileCopyrightText: 2022 Unikie cd /ext/svc/data @@ -11,16 +11,25 @@ if { if { mkdir /run/vm/${name} } if { ln -s /ext/svc/data/${name} /run/vm/${name}/config } + # Sockets for vhost-user backends need to be created before the VMM + # is started, because it will try to connect to them. + + # if { + # if -t { test -e ${name}/shared-dirs } + # cd ${name}/shared-dirs + # elglob -0 fsnames * + + # if { + # forx -po0 -E fsname { $fsnames } + # s6-instance-create /run/service/vhost-user-fs ${name}:${fsname} + # } + # s6-svwait -U /run/service/vhost-user-fs/instance/${name}:${fsnames} + # } + if { - if -t { test -e ${name}/shared-dirs } - cd ${name}/shared-dirs - elglob -0 fsnames * - - if { - forx -po0 -E fsname { $fsnames } - s6-instance-create /run/service/vhost-user-fs ${name}:${fsname} - } - s6-svwait -U /run/service/vhost-user-fs/instance/${name}:${fsnames} + if { mkdir /run/vm/${name}/fs } + if { s6-instance-create /run/service/vhost-user-fs $name } + s6-svwait -U /run/service/vhost-user-fs/instance/${name} } if { @@ -29,7 +38,13 @@ if { s6-svwait -U /run/service/vhost-user-gpu/instance/${name} } - s6-instance-create /run/service/vmm $name + if { s6-instance-create /run/service/vmm $name } + + # The service directory for the VMM needs to exist before + # xdg-desktop-portal-spectrum-host is started, so it can install its + # listening vsock socket. + if { s6-instance-create /run/service/xdg-desktop-portal-spectrum-host $name } + s6-svwait -U /run/service/xdg-desktop-portal-spectrum-host/instance/${name} } s6-svwait -U /run/service/vmm/instance/${names} diff --git a/host/rootfs/etc/s6-rc/weston/run b/host/rootfs/etc/s6-rc/weston/run index df2d74e..309eb0f 100644 --- a/host/rootfs/etc/s6-rc/weston/run +++ b/host/rootfs/etc/s6-rc/weston/run @@ -10,10 +10,7 @@ foreground { unexport ? backtick USER { id -un } -backtick HOME { - importas -i user USER - homeof $user -} +export HOME /run export XDG_RUNTIME_DIR /run/user/0 redirfd -r 0 /dev/tty1 diff --git a/host/rootfs/etc/xdg/weston/weston.ini b/host/rootfs/etc/xdg/weston/weston.ini index cdf8666..5595554 100644 --- a/host/rootfs/etc/xdg/weston/weston.ini +++ b/host/rootfs/etc/xdg/weston/weston.ini @@ -3,3 +3,7 @@ [autolaunch] path=/etc/xdg/weston/autolaunch + +[output] +name=Virtual-1 +mode=1920x1440 diff --git a/host/start-vmm/ch.rs b/host/start-vmm/ch.rs index cc05d84..9fa58a3 100644 --- a/host/start-vmm/ch.rs +++ b/host/start-vmm/ch.rs @@ -33,7 +33,7 @@ pub struct DiskConfig { #[derive(Serialize)] pub struct FsConfig { pub socket: String, - pub tag: String, + pub tag: &'static str, } #[derive(Serialize)] @@ -61,15 +61,22 @@ pub struct PayloadConfig { } #[derive(Serialize)] +pub struct VsockConfig { + pub cid: u32, + pub socket: &'static str, +} + +#[derive(Serialize)] pub struct VmConfig { pub console: ConsoleConfig, pub disks: Vec<DiskConfig>, - pub fs: Vec<FsConfig>, + pub fs: [FsConfig; 1], pub gpu: Vec<GpuConfig>, pub memory: MemoryConfig, pub net: Vec<NetConfig>, pub payload: PayloadConfig, pub serial: ConsoleConfig, + pub vsock: VsockConfig, } fn command(vm_name: &str, s: impl AsRef<OsStr>) -> Command { diff --git a/host/start-vmm/lib.rs b/host/start-vmm/lib.rs index 837e3d5..98dfaf5 100644 --- a/host/start-vmm/lib.rs +++ b/host/start-vmm/lib.rs @@ -18,7 +18,7 @@ use std::os::unix::process::parent_id; use std::path::Path; use std::process::{exit, Command}; -use ch::{ConsoleConfig, DiskConfig, FsConfig, GpuConfig, MemoryConfig, PayloadConfig, VmConfig}; +use ch::{ConsoleConfig, DiskConfig, FsConfig, GpuConfig, MemoryConfig, PayloadConfig, VmConfig, VsockConfig}; use fork::double_fork; use net::net_setup; use s6::notify_readiness; @@ -65,7 +65,7 @@ pub fn vm_config(vm_name: &str, config_root: &Path) -> Result<VmConfig, String> let blk_dir = config_dir.join("blk"); let kernel_path = config_dir.join("vmlinux"); let net_providers_dir = config_dir.join("providers/net"); - let shared_dirs_dir = config_dir.join("shared-dirs"); + // let shared_dirs_dir = config_dir.join("shared-dirs"); let wayland_path = config_dir.join("wayland"); Ok(VmConfig { @@ -102,27 +102,33 @@ pub fn vm_config(vm_name: &str, config_root: &Path) -> Result<VmConfig, String> .collect::<Result<_, _>>()?, Err(e) => return Err(format!("reading directory {:?}: {}", blk_dir, e)), }, - fs: match shared_dirs_dir.read_dir() { - Ok(entries) => entries - .into_iter() - .map(|result| { - let entry = result - .map_err(|e| format!("examining directory entry: {}", e))? - .file_name(); - - let entry = entry.to_str().ok_or_else(|| { - format!("shared directory name {:?} is not valid UTF-8", entry) - })?; - - Ok(FsConfig { - tag: entry.to_string(), - socket: format!("/run/service/vhost-user-fs/instance/{vm_name}:{entry}/env/virtiofsd.sock"), - }) - }) - .collect::<Result<_, String>>()?, - Err(e) if e.kind() == ErrorKind::NotFound => Default::default(), - Err(e) => return Err(format!("reading directory {:?}: {e}", shared_dirs_dir)), - }, + // fs: match shared_dirs_dir.read_dir() { + // Ok(entries) => entries + // .into_iter() + // .map(|result| { + // let entry = result + // .map_err(|e| format!("examining directory entry: {}", e))? + // .file_name(); + + // let entry = entry.to_str().ok_or_else(|| { + // format!("shared directory name {:?} is not valid UTF-8", entry) + // })?; + + // Ok(FsConfig { + // tag: entry.to_string(), + // socket: format!("/run/service/vhost-user-fs/instance/{vm_name}:{entry}/env/virtiofsd.sock"), + // }) + // }) + // .collect::<Result<_, String>>()?, + // Err(e) if e.kind() == ErrorKind::NotFound => Default::default(), + // Err(e) => return Err(format!("reading directory {:?}: {e}", shared_dirs_dir)), + // }, + fs: [ + FsConfig { + tag: "virtiofs0", + socket: format!("/run/service/vhost-user-fs/instance/{vm_name}/env/virtiofsd.sock"), + } + ], gpu: match wayland_path.try_exists() { Ok(true) => vec![GpuConfig { socket: format!("/run/service/vhost-user-gpu/instance/{vm_name}/env/crosvm.sock"), @@ -170,6 +176,10 @@ pub fn vm_config(vm_name: &str, config_root: &Path) -> Result<VmConfig, String> mode: "File", file: Some(format!("/run/{vm_name}.log")), }, + vsock: VsockConfig { + cid: 3, + socket: "env/vsock.sock", + }, }) } diff --git a/host/start-vmm/tests/meson.build b/host/start-vmm/tests/meson.build index 7f6bd08..1b4161f 100644 --- a/host/start-vmm/tests/meson.build +++ b/host/start-vmm/tests/meson.build @@ -37,7 +37,7 @@ test('vm_command-multiple-disks', executable('vm_command-multiple-disks', 'vm_command-multiple-disks.rs', dependencies : rust_lib_dep, link_with : rust_helper)) -test('vm_command-shared-dir', executable('vm_command-shared-dir', - 'vm_command-shared-dir.rs', - dependencies : rust_lib_dep, - link_with : rust_helper)) +# test('vm_command-shared-dir', executable('vm_command-shared-dir', +# 'vm_command-shared-dir.rs', +# dependencies : rust_lib_dep, +# link_with : rust_helper)) diff --git a/host/start-vmm/tests/vm_command-basic.rs b/host/start-vmm/tests/vm_command-basic.rs index 305e98c..0a533b8 100644 --- a/host/start-vmm/tests/vm_command-basic.rs +++ b/host/start-vmm/tests/vm_command-basic.rs @@ -31,6 +31,8 @@ fn main() -> std::io::Result<()> { assert!(config.memory.shared); assert_eq!(config.serial.mode, "File"); assert_eq!(config.serial.file.unwrap(), "/run/testvm.log"); + assert_eq!(config.vsock.cid, 3); + assert_eq!(config.vsock.socket, "env/vsock.sock"); Ok(()) } diff --git a/img/app/Makefile b/img/app/Makefile index 38df7d0..43751d1 100644 --- a/img/app/Makefile +++ b/img/app/Makefile @@ -27,6 +27,7 @@ $(imgdir)/appvm/blk/root.img: ../../scripts/make-gpt.sh ../../scripts/sfdisk-fie mv $@.tmp $@ VM_FILES = \ + etc/dbus-1/session.conf \ etc/fstab \ etc/init \ etc/mdev.conf \ @@ -39,7 +40,8 @@ VM_FILES = \ etc/s6-linux-init/env/DBUS_SESSION_BUS_ADDRESS \ etc/s6-linux-init/env/NIX_XDG_DESKTOP_PORTAL_DIR \ etc/s6-linux-init/env/XDG_RUNTIME_DIR \ - etc/s6-linux-init/scripts/rc.init + etc/s6-linux-init/scripts/rc.init \ + etc/xdg/xdg-desktop-portal/portals.conf VM_DIRS = dev run proc sys \ etc/s6-linux-init/env \ etc/s6-linux-init/run-image/ext \ @@ -125,13 +127,14 @@ run-qemu: $(imgdir)/appvm/blk/root.img start-virtiofsd -device virtio-gpu-rutabaga-pci,cross-domain=on,hostmem=8G \ -object memory-backend-memfd,id=mem,size=256M,share=on \ -numa node,memdev=mem \ + -device vhost-vsock-pci,guest-cid=3 \ -chardev vc,id=virtiocon0 \ -device virtio-serial-pci \ -device virtconsole,chardev=virtiocon0 .PHONY: run-qemu run-cloud-hypervisor: $(imgdir)/appvm/blk/root.img start-vhost-user-gpu start-virtiofsd - rm -f build/vmm.sock + rm -f build/vmm.sock build/vsock.sock @../../scripts/with-taps.elb ../../scripts/run-cloud-hypervisor.sh \ --api-socket path=build/vmm.sock \ --memory size=256M,shared=on \ @@ -139,10 +142,11 @@ run-cloud-hypervisor: $(imgdir)/appvm/blk/root.img start-vhost-user-gpu start-vi path=$(RUN_IMG),readonly=on \ --fs tag=virtiofs0,socket=build/virtiofsd.sock \ --gpu socket=build/vhost-user-gpu.sock \ + --vsock cid=3,socket=build/vsock.sock \ --net tap=tap0 \ --kernel $(KERNEL) \ --cmdline "root=PARTLABEL=root" \ - --console tty \ + --console pty \ --serial file=build/serial.log .PHONY: run-cloud-hypervisor diff --git a/img/app/default.nix b/img/app/default.nix index 1cc4719..220d2dd 100644 --- a/img/app/default.nix +++ b/img/app/default.nix @@ -1,13 +1,19 @@ # SPDX-License-Identifier: MIT -# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2021-2024 Alyssa Ross <hi@alyssa.is> import ../../lib/call-package.nix ( -{ lseek, src, terminfo, pkgsMusl, pkgsStatic }: +{ lseek, src +, terminfo, pkgsMusl, pkgsStatic +, systemd +}: + pkgsStatic.callPackage ( { lib, stdenvNoCC, runCommand, writeReferencesToFile , erofs-utils, jq, s6-rc, util-linux , busybox, cacert, dbus, execline, kmod, linux_latest, mdevd, s6, s6-linux-init +, xdg-desktop-portal-spectrum +, socat }: let @@ -15,6 +21,8 @@ let packages = [ dbus execline kmod mdevd s6 s6-linux-init s6-rc + xdg-desktop-portal-spectrum + pkgsMusl.strace socat (busybox.override { extraConfig = '' @@ -37,13 +45,20 @@ let inherit packages; passAsFile = [ "packages" ]; } '' - mkdir -p $out/usr/bin $out/usr/share/dbus-1/services + mkdir -p \ + $out/usr/bin \ + $out/usr/share/dbus-1/services \ + $out/usr/share/xdg-desktop-portal/portals ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/usr/bin + ln -s ${systemd}/bin/busctl $out/usr/bin ln -s ${dbus}/share/dbus-1/session.conf $out/usr/share/dbus-1 ln -st $out/usr/share/dbus-1/services \ ${pkgsMusl.xdg-desktop-portal}/share/dbus-1/services/*.service \ - ${pkgsMusl.xdg-desktop-portal-gtk}/share/dbus-1/services/*.service - ln -s ${pkgsMusl.xdg-desktop-portal-gtk}/share/xdg-desktop-portal $out/usr/share + ${pkgsMusl.xdg-desktop-portal-gtk}/share/dbus-1/services/*.service \ + ${xdg-desktop-portal-spectrum}/share/dbus-1/services/*.service + ln -st $out/usr/share/xdg-desktop-portal/portals \ + ${pkgsMusl.xdg-desktop-portal-gtk}/share/xdg-desktop-portal/portals/*.portal \ + ${xdg-desktop-portal-spectrum}/share/xdg-desktop-portal/portals/*.portal ln -s ${kernel}/lib "$out" ln -s ${terminfo}/share/terminfo $out/usr/share ln -s ${cacert}/etc/ssl $out/usr/share @@ -78,6 +93,14 @@ let VIRTIO_CONSOLE = yes; VIRTIO_PCI = yes; VT = no; + + VSOCKETS = yes; + VIRTIO_VSOCKETS_COMMON = yes; + VIRTIO_VSOCKETS = yes; + + AGP = no; + DRM_VIRTIO_GPU = yes; + DRM = yes; }; }).overrideAttrs ({ installFlags ? [], ... }: { installFlags = installFlags ++ [ diff --git a/img/app/etc/dbus-1/session.conf b/img/app/etc/dbus-1/session.conf new file mode 100644 index 0000000..d41f5a6 --- /dev/null +++ b/img/app/etc/dbus-1/session.conf @@ -0,0 +1,61 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <listen>vsock:</listen> + <listen>unix:path=/run/session-bus</listen> + + <auth>ANONYMOUS</auth> + <auth>EXTERNAL</auth> + <allow_anonymous /> + + <standard_session_servicedirs /> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <!-- Config files are placed here that among other things, + further restrict the above policy for specific services. --> + <includedir>session.d</includedir> + + <includedir>/etc/dbus-1/session.d</includedir> + + <!-- This is included last so local configuration can override what's + in this standard file --> + <include ignore_missing="yes">/etc/dbus-1/session-local.conf</include> + + <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include> + + <!-- For the session bus, override the default relatively-low limits + with essentially infinite limits, since the bus is just running + as the user anyway, using up bus resources is not something we need + to worry about. In some cases, we do set the limits lower than + "all available memory" if exceeding the limit is almost certainly a bug, + having the bus enforce a limit is nicer than a huge memory leak. But the + intent is that these limits should never be hit. --> + + <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max --> + <limit name="max_incoming_bytes">1000000000</limit> + <limit name="max_incoming_unix_fds">250000000</limit> + <limit name="max_outgoing_bytes">1000000000</limit> + <limit name="max_outgoing_unix_fds">250000000</limit> + <limit name="max_message_size">1000000000</limit> + <!-- We do not override max_message_unix_fds here since the in-kernel + limit is also relatively low --> + <limit name="service_start_timeout">120000</limit> + <limit name="auth_timeout">240000</limit> + <limit name="pending_fd_timeout">150000</limit> + <limit name="max_completed_connections">100000</limit> + <limit name="max_incomplete_connections">10000</limit> + <limit name="max_connections_per_user">100000</limit> + <limit name="max_pending_service_starts">10000</limit> + <limit name="max_names_per_connection">50000</limit> + <limit name="max_match_rules_per_connection">50000</limit> + <limit name="max_replies_per_connection">50000</limit> + +</busconfig> diff --git a/img/app/etc/s6-rc/dbus/run b/img/app/etc/s6-rc/dbus/run index 7c94772..53db196 100644 --- a/img/app/etc/s6-rc/dbus/run +++ b/img/app/etc/s6-rc/dbus/run @@ -2,10 +2,8 @@ # SPDX-License-Identifier: EUPL-1.2+ # SPDX-FileCopyrightText: 2023 Alyssa Ross <hi@alyssa.is> -importas -i address DBUS_SESSION_BUS_ADDRESS dbus-daemon - --address $address - --config-file /usr/share/dbus-1/session.conf + --config-file /etc/dbus-1/session.conf --nofork --print-address 3 diff --git a/img/app/etc/xdg/xdg-desktop-portal/portals.conf b/img/app/etc/xdg/xdg-desktop-portal/portals.conf new file mode 100644 index 0000000..9dce6ef --- /dev/null +++ b/img/app/etc/xdg/xdg-desktop-portal/portals.conf @@ -0,0 +1,3 @@ +[preferred] +default=gtk +org.freedesktop.impl.portal.FileChooser=spectrum diff --git a/pkgs/default.nix b/pkgs/default.nix index 850ac6a..032dbf9 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -39,6 +39,10 @@ let rootfs = self.callSpectrumPackage ../host/rootfs {}; start-vmm = self.callSpectrumPackage ../host/start-vmm {}; run-spectrum-vm = self.callSpectrumPackage ../scripts/run-spectrum-vm.nix {}; + xdg-desktop-portal-spectrum = + self.callSpectrumPackage ../tools/xdg-desktop-portal-spectrum {}; + xdg-desktop-portal-spectrum-host = + self.callSpectrumPackage ../tools/xdg-desktop-portal-spectrum-host {}; # Packages from the overlay, so it's possible to build them from # the CLI easily. diff --git a/tools/xdg-desktop-portal-spectrum-host/Cargo.lock b/tools/xdg-desktop-portal-spectrum-host/Cargo.lock new file mode 100644 index 0000000..1958fc5 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/Cargo.lock @@ -0,0 +1,1222 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.3.0", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +dependencies = [ + "async-lock 3.3.0", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +dependencies = [ + "async-channel", + "async-io", + "async-lock 3.3.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 5.1.0", + "futures-lite", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock 3.3.0", + "async-task", + "fastrand", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.1.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "xdg-desktop-portal-spectrum-host" +version = "0.1.0" +dependencies = [ + "async-executor", + "async-io", + "futures-lite", + "libc", + "percent-encoding", + "url", + "zbus", +] + +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "zbus" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b5c7bc69c56e09f4e379e250ae9028192e82aeaf7b9f8e72a86a2238a26d4c" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock 3.3.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener 5.1.0", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "067cc3a28ae806573727b8206a3a6a4718da55c54b2f70620a48fa92dd3bbc7d" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/tools/xdg-desktop-portal-spectrum-host/Cargo.toml b/tools/xdg-desktop-portal-spectrum-host/Cargo.toml new file mode 100644 index 0000000..aa41bf3 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "xdg-desktop-portal-spectrum-host" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-executor = "1.8.0" +async-io = "2.3.1" +futures-lite = "2.2.0" +libc = "0.2.153" +percent-encoding = "2.3.1" +url = "2.5.0" +zbus = { version = "4.1.0", features = ["p2p"] } diff --git a/tools/xdg-desktop-portal-spectrum-host/default.nix b/tools/xdg-desktop-portal-spectrum-host/default.nix new file mode 100644 index 0000000..c731bb4 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/default.nix @@ -0,0 +1,14 @@ +import ../../lib/call-package.nix ( +{ src, lib, rustPlatform }: + +rustPlatform.buildRustPackage { + name = "xdg-desktop-portal-spectrum-host"; + + src = lib.fileset.toSource { + root = ../..; + fileset = lib.fileset.intersection src ./.; + }; + sourceRoot = "source/tools/xdg-desktop-portal-spectrum-host"; + + cargoLock.lockFile = ./Cargo.lock; +}) (_: {}) diff --git a/tools/xdg-desktop-portal-spectrum-host/src/guest_dbus.rs b/tools/xdg-desktop-portal-spectrum-host/src/guest_dbus.rs new file mode 100644 index 0000000..a1a9e34 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/src/guest_dbus.rs @@ -0,0 +1,132 @@ +use std::collections::BTreeMap; +use std::ffi::OsString; +use std::os::unix::prelude::*; +use std::path::PathBuf; + +use percent_encoding::percent_decode; +use url::Url; +use zbus::zvariant::{Array, ObjectPath, OwnedValue, Value}; +use zbus::{interface, proxy}; + +use crate::{share_file, FILE_CHOOSER_PROXY}; + +#[proxy( + assume_defaults = false, + default_path = "/org/freedesktop/portal/desktop", + interface = "org.freedesktop.impl.portal.FileChooser" +)] +trait FileChooser { + fn open_file( + &self, + handle: &ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)>; + + fn save_file( + &self, + handle: &ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)>; + + fn save_files( + &self, + handle: &ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)>; +} + +#[derive(Debug)] +pub struct FileChooserImpl; + +#[interface(name = "org.freedesktop.impl.portal.FileChooser")] +impl FileChooserImpl { + async fn open_file( + &self, + handle: ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)> { + let (response, mut results) = FILE_CHOOSER_PROXY + .get() + .unwrap() + .open_file(&handle, app_id, parent_window, title, options) + .await?; + + eprintln!("results keys: {:?}", results.keys()); + + let writable = results + .get("writable") + .unwrap_or(&OwnedValue::from(false)) + .downcast_ref::<bool>() + .expect("writeable is not a bool"); + + if let Some(uris) = results.get("uris") { + let uris = uris.downcast_ref::<Array>().expect("uris is not an array"); + let mut uris: Vec<String> = uris.try_into().expect("uri is not a string"); + + for uri in uris.iter_mut() { + let path = uri.strip_prefix("file://").expect("uri is not a file:// URL"); + let path = percent_decode(path.as_bytes()); + let path = PathBuf::from(OsString::from_vec(path.collect())); + + share_file(path.clone(), writable); + + let mut guest_path = PathBuf::from("/run/virtiofs/virtiofs0"); + guest_path.push(path.file_name().expect("path has no file name")); + *uri = Url::from_file_path(guest_path).unwrap().to_string(); + } + + let uris: Array = uris.into(); + results.insert("uris".to_string(), OwnedValue::try_from(uris).unwrap()); + } + + Ok((response, results)) + } + + async fn save_file( + &self, + handle: ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)> { + dbg!(&handle, &app_id, &parent_window, &title, &options); + dbg!( + FILE_CHOOSER_PROXY + .get() + .unwrap() + .save_file(&handle, app_id, parent_window, title, options) + .await + ) + } + + async fn save_files( + &self, + handle: ObjectPath<'_>, + app_id: &str, + parent_window: &str, + title: &str, + options: BTreeMap<&str, Value<'_>>, + ) -> zbus::fdo::Result<(u32, BTreeMap<String, OwnedValue>)> { + dbg!(&handle, &app_id, &parent_window, &title, &options); + dbg!( + FILE_CHOOSER_PROXY + .get() + .unwrap() + .save_files(&handle, app_id, parent_window, title, options) + .await + ) + } +} diff --git a/tools/xdg-desktop-portal-spectrum-host/src/host_bus.rs b/tools/xdg-desktop-portal-spectrum-host/src/host_bus.rs new file mode 100644 index 0000000..70444f4 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/src/host_bus.rs @@ -0,0 +1,100 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +use std::future::{poll_fn, Future}; +use std::task::{Poll, Waker}; + +use zbus::{interface, Guid}; + +struct BusImpl { + name_owned: bool, + waker: Option<Waker>, +} + +pub struct Bus { + imp: Arc<Mutex<BusImpl>>, + name_owned_called: AtomicBool, +} + +const DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: u32 = 1; + +impl Bus { + pub fn new() -> Bus { + Self { + imp: Arc::new(Mutex::new(BusImpl { + name_owned: false, + waker: None, + })), + name_owned_called: AtomicBool::new(false), + } + } + + pub fn name_owned(&self) -> impl Future<Output = ()> { + if self.name_owned_called.swap(true, Ordering::SeqCst) { + panic!("Bus::name_owned can only be called once"); + } + + let imp = Arc::clone(&self.imp); + + poll_fn(move |context| { + let mut imp = imp.lock().unwrap(); + + if imp.name_owned { + Poll::Ready(()) + } else { + imp.waker = Some(context.waker().clone()); + Poll::Pending + } + }) + } +} + +#[interface(name = "org.freedesktop.DBus")] +impl Bus { + async fn hello(&self) -> zbus::fdo::Result<String> { + eprintln!("Hello"); + Ok(Guid::generate().to_string()) + } + + async fn add_match(&self, rule: &str) -> zbus::fdo::Result<()> { + eprintln!("AddMatch {:?}", rule); + Ok(()) + } + + async fn remove_match(&self, rule: &str) -> zbus::fdo::Result<()> { + eprintln!("RemoveMatch {:?}", rule); + Ok(()) + } + + async fn get_name_owner(&self, name: String) -> zbus::fdo::Result<String> { + eprintln!("GetNameOwner {:?}", name); + Err(zbus::fdo::Error::NameHasNoOwner(name)) + } + + async fn request_name(&mut self, name: &str, flags: u32) -> zbus::fdo::Result<u32> { + eprintln!("RequestName {:?} {:#x?}", name, flags); + + if flags > 0x7 { + let message = format!("unrecognized flags {:#x?}", flags & !0x7); + return Err(zbus::fdo::Error::NotSupported(message)); + } + + if name != "org.freedesktop.impl.portal.desktop.gtk" { + let message = format!("refusing to assign name {:?}", name); + return Err(zbus::fdo::Error::AccessDenied(message)); + } + + let mut imp = self.imp.lock().unwrap(); + imp.name_owned = true; + if let Some(waker) = imp.waker.take() { + waker.wake(); + } + + Ok(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + } + + async fn start_service_by_name(&self, name: String, flags: u32) -> zbus::fdo::Result<u32> { + eprintln!("StartServiceByName {:?} {:#x?}", name, flags); + Err(zbus::fdo::Error::ServiceUnknown(name)) + } +} diff --git a/tools/xdg-desktop-portal-spectrum-host/src/main.rs b/tools/xdg-desktop-portal-spectrum-host/src/main.rs new file mode 100644 index 0000000..c91a5d4 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum-host/src/main.rs @@ -0,0 +1,272 @@ +mod guest_dbus; +mod host_bus; + +use std::cmp::max; +use std::env::args_os; +use std::ffi::{CString, OsStr, OsString}; +use std::fs::File; +use std::io::{self, ErrorKind}; +use std::os::linux::net::SocketAddrExt; +use std::os::unix::net::{SocketAddr, UnixListener, UnixStream}; +use std::os::unix::prelude::*; +use std::path::{Path, PathBuf}; +use std::ptr; +use std::process::{exit, Command}; +use std::slice; +use std::sync::OnceLock; + +use async_executor::Executor; +use async_io::Async; +use futures_lite::prelude::*; +use futures_lite::stream::StreamExt; +use libc::{mount, MS_BIND, MS_REC}; +use zbus::{ConnectionBuilder, Guid, MessageStream}; + +use guest_dbus::{FileChooserImpl, FileChooserProxy}; + +static VSOCK_UNIX_PATH: OnceLock<PathBuf> = OnceLock::new(); +static FILE_CHOOSER_PROXY: OnceLock<FileChooserProxy> = OnceLock::new(); + +fn share_file(source_path: PathBuf, writable: bool) { + assert!(source_path.is_file()); + assert!(writable); + + let fs_root_dir = std::env::var_os("XDG_DESKTOP_PORTAL_SPECTRUM_HOST_FS_ROOT").unwrap(); + let name = source_path.file_name().unwrap(); + + let dest_path = Path::new(&fs_root_dir).join(name); + + File::create(&dest_path).unwrap(); + + let c_source_path = CString::new(source_path.into_os_string().into_vec()).unwrap(); + let c_dest_path = CString::new(dest_path.into_os_string().into_vec()).unwrap(); + + unsafe { + if mount(c_source_path.as_ptr(), c_dest_path.as_ptr(), ptr::null(), MS_BIND|MS_REC, ptr::null()) == -1 { + panic!("{}", io::Error::last_os_error()); + } + } +} + +async fn negotiate_version(conn: &mut Async<UnixStream>) -> Result<(), String> { + let mut num_versions = 0; + conn.read_exact(slice::from_mut(&mut num_versions)) + .await + .map_err(|e| format!("reading number of versions supported by client: {e}"))?; + + let mut client_versions = vec![0; num_versions.into()]; + conn.read_exact(&mut client_versions) + .await + .map_err(|e| format!("reading versions supported by client: {e}"))?; + + if !client_versions.contains(&1) { + let msg = format!( + "no supported protocol versions offered by client: {:?}", + client_versions + ); + return Err(msg); + } + + conn.write_all(&[1]) + .await + .map_err(|e| format!("communicating chosen protocol version to client: {e}"))?; + + Ok(()) +} + +async fn receive_port(conn: &mut Async<UnixStream>) -> Result<u32, String> { + negotiate_version(conn).await?; + + let mut port = [0; 4]; + conn.read_exact(&mut port) + .await + .map_err(|e| format!("reading port: {e}"))?; + + if let Ok(1) = conn.read(&mut [0]).await { + return Err("unexpected data following port".to_string()); + } + + Ok(u32::from_be_bytes(port)) +} + +async fn connect_to_guest(port: u32) -> Result<Async<UnixStream>, String> { + let vsock_unix_path = VSOCK_UNIX_PATH.get().unwrap(); + let mut vsock = Async::<UnixStream>::connect(vsock_unix_path) + .await + .map_err(|e| format!("connecting to {:?}: {e}", vsock_unix_path))?; + for part in [b"CONNECT ", port.to_string().as_bytes(), b"\n"] { + vsock + .write_all(part) + .await + .map_err(|e| format!("writing to VSOCK UNIX socket: {e}"))?; + } + + // TODO: this shouldn't be an unwrap, because it could actually happen. + // TODO: MSG_PEEK? + let mut response = [0; 14]; // Length of "OK [u32::MAX]\n" is 14. + let min_response_len = "OK 0\n".len(); + let mut response_len = 0usize; + + while response_len.checked_sub(1).and_then(|i| response.get(i)) != Some(&b'\n') + && response_len < response.len() + { + let min_remaining = min_response_len.saturating_sub(response_len); + let end = max(min_remaining, response_len + 1); + let dest = response.get_mut(response_len..end).unwrap(); + response_len += dest.len(); + vsock + .read_exact(dest) + .await + .map_err(|e| format!("reading VSOCK connection response: {e}"))?; + } + + if !response.starts_with(b"OK ") { + return Err(format!( + "unexpected response from Cloud Hypervisor VSOCK handshake: {:#x?}", + response + )); + } + + Ok(vsock) +} + +async fn run_guest_connection(mut conn: Async<UnixStream>) -> Result<(), String> { + let port = receive_port(&mut conn).await?; + let vsock = connect_to_guest(port).await?; + + let guest_conn = ConnectionBuilder::socket(vsock) + .name("org.freedesktop.impl.portal.desktop.spectrum") + .unwrap() + .serve_at("/org/freedesktop/portal/desktop", FileChooserImpl) + .unwrap() + .build() + .await + .map_err(|e| format!("setting up connection to guest bus: {e}"))?; + + eprintln!("Created org.freedesktop.impl.portal.desktop.spectrum.host on guest bus"); + + let mut guest_messages = MessageStream::from(guest_conn); + loop { + match guest_messages.try_next().await { + Ok(_) => (), + Err(zbus::Error::InputOutput(e)) if e.kind() == ErrorKind::BrokenPipe => break Ok(()), + Err(e) => return Err(format!("communicating with guest bus: {e}")), + } + } +} + +fn read_argv() { + let mut args = args_os(); + args.next(); + + if args.next().is_some() { + eprintln!("too many arguments"); + exit(1); + } +} + +async fn spawn_portal_impl() -> Result<(), Box<dyn std::error::Error>> { + let impl_conn_guid = Guid::generate(); + let addr = SocketAddr::from_abstract_name(impl_conn_guid.as_bytes())?; + let impl_listener = Async::new(UnixListener::bind_addr(&addr)?)?; + + let bus_addr = format!("unix:abstract={impl_conn_guid}"); + Command::new("xdg-desktop-portal-gtk") + .env("DBUS_SESSION_BUS_ADDRESS", &bus_addr) + .spawn()?; + + let (impl_conn, _) = impl_listener.accept().await?; + + let host_bus = host_bus::Bus::new(); + let name_owned = host_bus.name_owned(); + + let impl_conn = ConnectionBuilder::socket(impl_conn) + .p2p() + .server(Guid::generate()) + .unwrap() + .serve_at("/org/freedesktop/DBus", host_bus)? + .build() + .await?; + + name_owned.await; + + FILE_CHOOSER_PROXY + .set(FileChooserProxy::new(&impl_conn, "org.freedesktop.impl.portal.desktop.gtk").await?) + .unwrap(); + + Ok(()) +} + +fn listening_vsock_path(connection: &UnixListener) -> io::Result<PathBuf> { + let Some(mut listening_addr) = connection + .local_addr()? + .as_pathname() + .map(Path::as_os_str) + .map(OsStr::to_os_string) + .map(OsString::into_vec) + else { + eprintln!("stdin is not listening on a path"); + exit(1); + }; + + let mut i = listening_addr.len() - 1; + + loop { + match (i != 0).then_some(listening_addr[i]) { + Some(b'0'..=b'9') => { + i -= 1; + } + Some(b'_') => { + break; + } + _ => { + let os_string = OsString::from_vec(listening_addr); + eprintln!("can't infer listening VSOCK path from {:?}", os_string); + exit(1); + } + } + } + + listening_addr.truncate(i); + Ok(OsString::from_vec(listening_addr).into()) +} + +// TODO: let's not have main return a Result. +fn main() -> Result<(), Box<dyn std::error::Error>> { + read_argv(); + + let ex = Executor::new(); + + async_io::block_on(ex.run(async { + // SAFETY: safe because we won't use fd 0 anywhere else. + let stdin = Async::try_from(unsafe { UnixListener::from_raw_fd(0) })?; + + VSOCK_UNIX_PATH + .set(listening_vsock_path(stdin.get_ref())?) + .unwrap(); + + let mut portal_impl_spawned = false; + + loop { + let (conn, _) = match stdin.accept().await { + Ok(conn) => conn, + Err(e) => { + eprintln!("accepting connection from guest: {e}"); + continue; + } + }; + + if !portal_impl_spawned { + spawn_portal_impl().await?; + portal_impl_spawned = true; + } + + ex.spawn(async move { + if let Err(e) = run_guest_connection(conn).await { + eprintln!("guest connection error: {e}"); + } + }) + .detach(); + } + })) +} diff --git a/tools/xdg-desktop-portal-spectrum/default.nix b/tools/xdg-desktop-portal-spectrum/default.nix new file mode 100644 index 0000000..41d34bc --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum/default.nix @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2022-2023 Alyssa Ross <hi@alyssa.is> + +import ../../lib/call-package.nix ( +{ src, lib, stdenv, meson, ninja, pkg-config, dbus }: + +stdenv.mkDerivation (finalAttrs: { + name = "xdg-desktop-portal-spectrum"; + + src = lib.fileset.toSource { + root = ../..; + fileset = lib.fileset.intersection src ./.; + }; + sourceRoot = "source/tools/xdg-desktop-portal-spectrum"; + + nativeBuildInputs = [ meson ninja pkg-config ]; + buildInputs = [ dbus ]; + + # TODO: clang-tidy +}) +) (_: {}) diff --git a/tools/xdg-desktop-portal-spectrum/meson.build b/tools/xdg-desktop-portal-spectrum/meson.build new file mode 100644 index 0000000..9303629 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum/meson.build @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2024 Alyssa Ross <hi@alyssa.is> + +project('xdg-desktop-portal-spectrum', 'c', + default_options : [ 'warning_level=3' ]) + +add_project_arguments('-D_GNU_SOURCE', language : 'c') + +dbus = dependency('dbus-1') + +executable('xdg-desktop-portal-spectrum', 'xdg-desktop-portal-spectrum.c', + dependencies : dbus, + install : true) + +install_data('spectrum.portal', + install_dir : get_option('datadir') / 'xdg-desktop-portal/portals') + +conf_data = configuration_data() +conf_data.set('bindir', get_option('prefix') / get_option('bindir')) +configure_file( + input : 'org.freedesktop.impl.portal.desktop.spectrum.service.in', + output : 'org.freedesktop.impl.portal.desktop.spectrum.service', + configuration : conf_data, + install_dir : get_option('datadir') / 'dbus-1/services') diff --git a/tools/xdg-desktop-portal-spectrum/org.freedesktop.impl.portal.desktop.spectrum.service.in b/tools/xdg-desktop-portal-spectrum/org.freedesktop.impl.portal.desktop.spectrum.service.in new file mode 100644 index 0000000..9c4a2cd --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum/org.freedesktop.impl.portal.desktop.spectrum.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.impl.portal.desktop.spectrum +Exec=@bindir@/xdg-desktop-portal-spectrum diff --git a/tools/xdg-desktop-portal-spectrum/spectrum.portal b/tools/xdg-desktop-portal-spectrum/spectrum.portal new file mode 100644 index 0000000..08e3eb7 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum/spectrum.portal @@ -0,0 +1,3 @@ +[portal] +DBusName=org.freedesktop.impl.portal.desktop.spectrum +Interfaces=org.freedesktop.impl.portal.FileChooser diff --git a/tools/xdg-desktop-portal-spectrum/xdg-desktop-portal-spectrum.c b/tools/xdg-desktop-portal-spectrum/xdg-desktop-portal-spectrum.c new file mode 100644 index 0000000..24c40b3 --- /dev/null +++ b/tools/xdg-desktop-portal-spectrum/xdg-desktop-portal-spectrum.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2024 Alyssa Ross <hi@alyssa.is> + +#include <arpa/inet.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <sys/stat.h> + +#include <linux/vm_sockets.h> + +#include <dbus/dbus.h> + +static const uint32_t HOST_PORT = 219; + +static int parse_u32(const char *s, uint32_t *v) +{ + char *end; + + errno = EINVAL; + if (!s || !isdigit(s[0])) + return -1; + + errno = 0; + *v = strtol(s, &end, 10); + if (errno) + return -1; + if (*end) { + errno = EINVAL; + return -1; + } + + return 0; +} + +static int write_all(int fd, const void *buf, size_t len) +{ + int r; + + do { + r = write(fd, buf, len); + if (r == -1) + return -1; + buf = (char *)buf + r; + len -= r; + } while (len); + + return 0; +} + +static void send_port(uint32_t port) +{ + struct sockaddr_vm addr = { + .svm_family = AF_VSOCK, + .svm_cid = VMADDR_CID_HOST, + .svm_port = HOST_PORT, + }; + char handshake[] = { 1, 1 }; + char version; + int sock = socket(AF_VSOCK, SOCK_STREAM, 0); + + port = htonl(port); + + if (sock == -1) + err(EXIT_FAILURE, "creating vsock socket"); + + if (connect(sock, (struct sockaddr *)&addr, sizeof addr) == -1) + err(EXIT_FAILURE, "connecting to cid %" PRIu32 " port %" PRIu32, + addr.svm_cid, addr.svm_port); + + if (write_all(sock, handshake, sizeof handshake) == -1) + err(EXIT_FAILURE, "writing handshake to vsock socket"); + + if (read(sock, &version, 1) == -1) + err(EXIT_FAILURE, "reading handshake version"); + if (version != 1) + err(EXIT_FAILURE, "unexpected protocol version %d", version); + + if (write_all(sock, &port, sizeof port) == -1) + err(EXIT_FAILURE, "writing port to vsock socket"); +} + +int main(void) +{ + char *addr = getenv("DBUS_STARTER_ADDRESS"); + + DBusAddressEntry **entries; + int entries_len, i; + DBusError error; + + const char *port_str; + uint32_t port; + + if (!addr) + errx(EXIT_FAILURE, "DBUS_STARTER_ADDRESS not set"); + + warnx("DBUS_STARTER_ADDRESS: '%s'", addr); + + if (!dbus_parse_address(addr, &entries, &entries_len, &error)) + errx(EXIT_FAILURE, "parsing D-Bus address '%s': %s", + addr, error.message); + + for (i = 0; i < entries_len; i++) { + if (strcmp(dbus_address_entry_get_method(entries[i]), "vsock")) + continue; + + if (!(port_str = dbus_address_entry_get_value(entries[i], "port"))) + errx(EXIT_FAILURE, "missing vsock port in D-Bus address '%s'", + addr); + + if (parse_u32(port_str, &port) == -1) + err(EXIT_FAILURE, "D-Bus address vsock port"); + + send_port(port); + return 0; + } +} |