It has been suggested that it would be a good idea to support xdg portals as used by flatpak to facilitate host⇄confinement interaction for common use cases such as opening a URI or opening a file.
The advantage is that the wheel doesn’t need to get reinvented for snapd as this use case and the way it should work can be exactly the same regardless of the actual distribution/confinement tech. Be it flatpak or snap or whatever.
The biggest limitation of snaps we experience right now is that they can’t get access to host files. xdg-desktop-portals solves this problem nicely and in addition, adds some cross-desktop dbus API to do common desktop application stuff such as sending a notification.
The way it works is that the confined application needs access to the portal service on the dbus session bus under org.freedesktop.portal.Desktop. This service is provided by the xdg-desktop-portal helper. The helper then uses a backend such as the kdeframeworks one to carry out user interaction using host-mechanism rather than snap mechanism (e.g. the host opens the file-open dialog rather than the snapped app itself).
So, from a snapd POV all it would need to get started is a xdg-desktop-portal interface giving access to org.freedesktop.portal.* for snaps. Technically a more fine-grained interface set up may be possible (i.e. since the abilities of the portal are scoped by separate interfaces such as org.freedesktop.portal.Print and org.freedesktop.portal.Screenshot there could also be an interface only giving access to screenshotting).
As a second step the existing code of xdg-desktop-portal probably should be revised for flatpakism. I’ve already seen an execve to flatpak run and hardcoded use of flatpak’s metadata. These cases seem fairly isolated, so it’s more a matter of finding them and adding logic to attempt using snap if/when applicable.
Lastly, file IO needs to be looked at. From a quick look at the relevant code it seems to rely on a metadata file .flatpak-info in /proc/$pid/root to get a bunch of data (naturally that probably should grow support for snap metadata). The most relevant date is the “app_id” which in turn is used by the documents system to provision the file access. If all goes well the helper comes back to the contained application with a URI in /run/user/$UID/doc/, so this will also need to be read-writable trough the snapd interface. Additionally, I think “app_id” results in further nesting within that /run path to isolate apps from one another. That’s just a guess though so some testing is in order there
If I’m reading this correctly, it’s the xdg-desktop-portal helper that copies or symlinks the selected file to /run/user/$UID/doc/$app_id/ and then returns a file path to that as the response to the initial dbus call?
If that is the case, then the only additional access (other than dbus) that would be needed is read/write access to that directory path, which should be easy enough to do.
It sounds like the best approach is to define a new interface, call it xdg-desktop-portal, that provides both the limited dbus access needed for this, as well as the read/write access to the path where the chosen files will be copied. @zyga-snapd does this sound right to you?
I understand you’d like to improve the user experience in graphic applications, which is a noble goal. It’s important we clarify what is or is not possible with snaps, though.
In particular, the stated goal is not by itself an actual limitation in snaps. There are today multiple ways in which files and resources of all kinds from the host may be made available to snaps. Not only that, but snapd can also easily be extended to access data via pretty much any method that the hosting system supports. We already have backends and interfaces interacting with dbus, apparmor, seccomp, mount subsystem, and a few others today.
With that out of the way, fine tuned support for xdg-desktop-portal and any other intermediation mechanism that real applications depend on is very welcome. Similar to how deb packages, or rpm packages don’t really say what APIs we’re supposed to be using in your applications, snaps won’t constrain us to any particular API either. If the application wants to use a dbus API to ask for a file, that’s find and trivial today. If it wants to do an old school open system call, there are good ways to mediate that as well.
That sounds easy and fine.
It might also not even be necessary. We recently landed a general dbus interface which allows declaring in its definition what specific resources are being used across the wire. A first class interface might be more convenient for people, though.
That part seems suspect. The application (and the XDG protocol?) shouldn’t have to know how it’s being packaged to tell how to request access to a file.
Sounds reasonable and simple.
Please let us know how you’d like to proceed. Would you like to try cooking a strawman interface?
Unfortunately, I ran into some problems getting the document portal bind mount working. Like I said in that forum thread, I don’t think it is reasonable to try and reimplement any of the xdg-desktop-portal services: toolkits are expecting the full suite of services to be available, and matching the semantics of things like the document portal would be non-trivial.
I put together some notes on how we could go about integrating the code here:
We are going to need to alter the xdg-desktop-portal code to detect when it is talking to a snap confined application, and determine its application ID. So no distro release contains a usable version of the service. So getting it working on 14.04 would be the same as for any other release: package it up and get it in the updates repository.
Yes, this is more complex than a snap-specific URL opener solution, but it solves many more problems:
allow apps to open and save files to any location without using the home interface.
inhibit screensavers
check network state without talking directly to NetworkManager
display notifications
print documents
access the user’s web proxy configuration
take screenshots
On top of this, there is already GTK and Qt integration for all of this, on both the sandbox and host system sides.
We’re close to having some useful xdg-desktop-portal support in snapd now. Once the following PR is merged, we should have enough to use the file open/save portal APIs:
Build and install snapd from my branch (or wait for it to be merged to master)
Install xdg-desktop-portal >= 0.11
Install xdg-desktop-portal-gtk
Install the portal-test snap from the edge channel: snap install --edge portal-test
Now as the user ensure that the document portal is running (snap run won’t automatically start it yet) so that the $XDG_RUNTIME_DIR/doc mount is available:
systemctl --user start xdg-document-portal
On my system running Wayland, I needed to run xdg-desktop-portal-gtk in X11-only mode too since it was getting disconnected due to protocol errors: I need to do some more testing to see if this is a problem unique to my setup or not though. This can be achieved by running the following in a terminal:
Now run portal-test in another terminal. Click the “Save as…” button, which should show a file chooser that can see everything an unconfined app can. Pick a location to write a file, and portal-test should write the content “test” to that location. It does all this without using the home interface.
Under the covers, the process looks something like this:
portal-test informs xdg-desktop-portal that it wants to save a file.
xdg-desktop-portal asks xdg-desktop-portal-gtk to show the file picker and return the user’s selected file name.
xdg-desktop-portal registers that file path with xdg-document-portal. This makes it available at a path like /run/user/$uid/doc/$docid/$filename
it also asks xdg-document-portal to make the file available to portal-test, which also makes the file available at `/run/user/$uid/doc/by-app/snap.portal-test/$docid/$filename
xdg-desktop-portal tells portal-test that it can write to /run/user/$uid/doc/$docid/$filename.
Due to a bind mount in portal-test’s mount namespace, this actually maps to the .../by-app/... path, so it is restricted to documents the app has access to.
portal-test opens the provided path, which the document portal FUSE file system splices through to the real location.
As I see it, there are still a few things to finish this off:
snap run should launch xdg-document-portal prior to calling snap-confine when a snap needs it.
xdg-desktop-portal needs some more metadata about snaps. As previously discussed, this is probably easiest to implement as an extra command on the snap executable that provides the required metadata.
In particular, we need to be able to determine a desktop file ID for the snap, since we currently end up using snap.$pkgname.desktop in some places which causes some problems (e.g. no app name in dialog, failures in notifications portal, etc)
document what snaps need to do in order to use portals. Some of this can be done via snapcraft-desktop-helpers, but it generally needs a newer GTK than found in xenial. It either needs the GNOME platform snap/backport PPA, or build with bionic once that’s sorted out.
I’ve tested this with xdg-desktop-portal from https://launchpad.net/~alexlarsson/+archive/ubuntu/flatpak + KDE neon’s build of xdg-desktop-portal-kde (Plama 5.12), inside the snap we have Qt 5.10 and the integration plugin (Plasma 5.12) coming from the kde-frameworks content snap.
It seems to work very well indeed. Good job!
There’s some UX problems with save dialog going to the /run/user/… dir by default though; I am not quite sure who’s at fault for that.
Can be tested with regular okular snap by opening a pdf and saving it under a different name (printing doesn’t actually use portals for technical reasons inside okular):
snap install okular
QT_QPA_PLATFORMTHEME=flatpak snap run okular
Setting that env var is actually all one needs to do to portal any existing KDE snap, in theory anyway.
There are some issues (that do not affect okular) with Qt 5.10 that are solved in 5.11. However, I’ve just had a look and the way Qt decides whether to use portals seems unsuitable for snaps. If this was fixed one also wouldn’t need to set the env var as Qt would automatically adjust for the environment own its own.
Basically it seems to go after a special marker file which flatpaks would have in their run dir, but we do not have in snaps.
Additionally, I suppose from a snap perspective snaps do not necessarily have or need to use portals, so I am wondering how Qt would tell whether to use portals in a snap at all. I am not sure it matters though. On an unconfined snap using portals only has the downside of being slower, on a confined snap not using portals likely means that the feature in question is broken anyway unless portals are used.
Any thoughts on how to deal with this?
@jamesh - I wonder if Qt shouldn’t be in the business of detecting portal support and instead, snapd (or possibly the desktop part) can interrogate the system and export the env var that to say it should use portals…
Re. Saving files going to /run/user/…
This should be correct behaviour in case the app doesn’t have access to the directory where this file is supposed to be saved. If it’s this case, the file goes through document portal, but in the end it should be saved on the desired location. If you have access to the directory, then there was an issue some time ago in flatpak or xdg-desktop-portal where Flatpak didn’t return real path to the file and it always went through document portal.
Re. Automatic use of Flatpak platform theme
You probably want to extend that condition and add your check for snap as well. The platform theme is not the only place where portal support is implemented so you need other stuff besides the platform theme. You also don’t want to use QT_PLATFORM_THEME variable to force to use flatpak platform theme, as internally this platform theme loads “native” platform themes (like KDE one) and if one wants to specify a platform theme using this variable and have flatpak/snap support on top of that, then it won’t be possible. One condition for flatpak support is in QGenericUnixServices and the second one is the one mentioned by @sitter in QGuiApplicationPrivate.
Otherwise, current state of portals is that most of them are upstreamed and released with Qt 5.11, but there are also (currently) two other contributions ([1], [2]) made for Qt 5.12 which are not in 5.11 and you need them for proper support. Only missing portal, which is not implemented on the Qt side is printing, we have only backend part so your only chance is to save the document to pdf and print it outside sandbox.
Although in GTK’s case it is also possible to enable the use of portals through the GTK_USE_PORTAL environment variable, which is what I’ve used in the portal-test snap.
Longer term, it’d be good to have a more generic “should I use portals?” detection mechanic. Whether that involves an environment variable set by the confinement system, or some flag set in the sandbox’s mount namespace (as flatpak is currently doing) is an open question.