Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow host apps to register themselves #1521

Merged
merged 17 commits into from
Jan 27, 2025

Conversation

jadahl
Copy link
Collaborator

@jadahl jadahl commented Dec 2, 2024

For host apps, we currently require applications to run inside a cgroup with a name that allows xdg-desktop-portal to derive an application ID from it. This does not work reliably, and while the situation can improve, it's unlikely to ever be fool proof, as it in theory requires every potential piece of software that launches other applications to know how to launch it in a compatible way.

Cgroups is also a Linux specific API, which is problematic from a *nix wide portability perspective.

To address this, introduce a host-only portal API that allows applications to register their D-Bus connections, associating them with an application ID. This moves back the burden of associating an application with an application ID back to the application itself, instead of the application launcher.

Applications calling this overrides any applicion ID derived from the name fo the cgroup the application runs inside.


I'm not saying we should do this, but perhaps it is something we should consider doing. Relying only on cgroups is a noble idea, but it hasn't worked well so far, and while it's possible that at least a majority of cases can be handled by peeking at it and deriving the application ID, it's also likely that there will always be cases where it won't work, without any way for applications to get themselves to work.

Some downsides include:

  • Giving up on the idea to always have host apps run in a properly named cgroup, at least for any portal purposes. It's a good end goal in itself, but hard to say how reliable it can ever be.
  • Yet again more than one way to get the same piece of information, as in we're creating another mess for ourself.

Some upsides:

  • Applications can now fix themselves. They can't realistically do that by relying on cgroups.
  • Not Linux specific. While sandbox engines probably are, it's a good end goal to have portal API's be as widely adopted as possible.

@jadahl jadahl force-pushed the wip/host-portal-registry branch from 6c33ebe to cf508ac Compare December 2, 2024 21:31
@ebassi
Copy link
Collaborator

ebassi commented Dec 3, 2024

Applications can now fix themselves. They can't realistically do that by relying on cgroups.

As much as I admire your optimism, I have a pretty hard time believing apps will "fix themselves" by calling a D-Bus API (or using libportal) for the unsandboxed case. You're still asking application developers to deal with this case. Might as well ask them to call:

setenv ("XDP_PROP_application_id", "com.example.App");

in their main(), like Pulseaudio requires, and then have every single portal-aware library check for that environment variable.

@swick
Copy link
Collaborator

swick commented Dec 3, 2024

One could equally argue that apps only have to call StartTransientUnit("app-none-{appid}", pid()) in its main but that also hasn't happened so far...

@jadahl
Copy link
Collaborator Author

jadahl commented Dec 3, 2024

As much as I admire your optimism, I have a pretty hard time believing apps will "fix themselves" by calling a D-Bus API (or using libportal) for the unsandboxed case. You're still asking application developers to deal with this case. Might as well ask them to call:

For apps that wants to support running non-sandboxed that uses libportal or ashpd could perhaps use API like xdp_portal_set_fallback_app_id(). No need to go via env vars for that.

For apps that interact directly with the portal using D-Bus it seems like a trivial thing to make just another method call before anything else.

One could equally argue that apps only have to call StartTransientUnit("app-none-{appid}", pid()) in its main but that also hasn't happened so far...

Using StartTransientUnit() is a bit problematic since it might already be in a proper unit, not to mention it's systemd specific, which might hinder adoption of portal APIs as "universal". I also imagine app developers are more hesitant to start to become self-service managers like that, compared to just passing a bit of metadata.

@swick
Copy link
Collaborator

swick commented Dec 3, 2024

Using StartTransientUnit() is a bit problematic since it might already be in a proper unit

One could check that and skip StartTransientUnit().

Anyway, I do agree that calling one more portal helper function or calling one more dbus method when one wants to interact with portals shouldn't be much of an issue.

@hsitter
Copy link
Contributor

hsitter commented Dec 18, 2024

@Sodivad and I were musing on this a bit from the KDE side of things. It generally seems like a good idea, so +1 from us

A getter might be handy for the unforeseen scenario where the app (e.g. inside a library) wants to know its own app id.

It'd be good to document whether the registration is per-pid or per-connection (e.g. say I open multiple dbus connections in my app - do I want to make sure that all of them are registered with the portal or is one sufficient so the pid is associated?)

Maybe also document what happens when Register is called more than once (possibly with conflicting information?).

@jadahl
Copy link
Collaborator Author

jadahl commented Dec 18, 2024

A getter might be handy for the unforeseen scenario where the app (e.g. inside a library) wants to know its own app id.

Seems like a harmless thing to add. Apps must know it's only a host-app thing though, since this API namespace won't be available in sandboxes.

It'd be good to document whether the registration is per-pid or per-connection (e.g. say I open multiple dbus connections in my app - do I want to make sure that all of them are registered with the portal or is one sufficient so the pid is associated?)

That is a good question. Making it per pid makes sense unless apps expect to be able to have different hats from the same process. Seems unlikely, and simply impossible would they run in a sandbox.

@Sodivad
Copy link
Contributor

Sodivad commented Dec 18, 2024

That is a good question. Making it per pid makes sense unless apps expect to be able to have different hats from the same process. Seems unlikely, and simply impossible would they run in a sandbox.

The famous one would be Libre Office where afaiu everything is a single process even windows can appear as differnt apps writer, calc, impress..

@jadahl
Copy link
Collaborator Author

jadahl commented Dec 18, 2024

The famous one would be Libre Office where afaiu everything is a single process even windows can appear as differnt apps writer, calc, impress..

Then perhaps for consistency, it should still be per pid here still, so that it doesn't work all too differently running LibreOffice inside or outside of a sandbox, to avoid both developer and user confusion.

@hsitter
Copy link
Contributor

hsitter commented Dec 19, 2024

Sounds reasonable 👍

@checkraisefold
Copy link

This would solve the massive headache of launching apps from terminal windows giving incorrect app IDs and absolutely requiring either a wrapper to execute an app or launching the .desktop through a graphical interface

When working on global shortcuts portal adoption, it's not been great running an app from a terminal window, or as a child process of something, and then it has a blank appid so the portal backend either doesn't register your shortcuts or it generates a random appid based on the session token. In KDE specifically, you will then have a random token as the app name in the system settings, which is pretty bad for UX. It turns out most apps don't run their children in spec conformant systemd units

I have a feeling though that the automatic app id registration should be kept as a fallback in case an app doesn't register an app id. App ID being guaranteed to match a desktop entry by default, or being empty, (or flatpak/snap) is useful for fetching display names from the .desktop and other info automagically, which is now done in xdp-kde (see https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/merge_requests/341). But an app not being able to register its own app id is a much bigger headache than not being able to fetch a display name.

@jadahl jadahl force-pushed the wip/host-portal-registry branch from cf508ac to ed8ac44 Compare January 14, 2025 12:52
@jadahl jadahl changed the title RFC: Allow host apps to register themselves Allow host apps to register themselves Jan 14, 2025
@jadahl
Copy link
Collaborator Author

jadahl commented Jan 14, 2025

Pushed an implementation, but now noticed we had discussed pid vs peer:

Then perhaps for consistency, it should still be per pid here still, so that it doesn't work all too differently running LibreOffice inside or outside of a sandbox, to avoid both developer and user confusion.

It's not yet doing that, and doing so will already address issues I ran into when updating libportal's qt examples, where the app ID for connections from Qt resolve differently to the libportal connection.


if (!app_info)
{
gboolean is_sandboxed = FALSE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of having this duplication we could call xdp_connection_create_app_info_sync and xdp_app_info_is_host.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that in an earlier iteration, but changed to this approach to not have this code path go through potential creation of non-host apps. I think this ended up nicer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that undesirable? Creating XdpAppInfo's is side-effect free.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, trying to fully fledged objects, when there were no intention to use them, since only the condition for them to be created was of interest, wasn't nice. The relevant condition also became somewhat hidden. Thus I ditched complicating create_app_info_sync() and made the paths separate.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -867,25 +866,19 @@ xdp_connection_create_host_app_info_sync (GDBusConnection  *connection,
 
   if (!app_info)
     {
-      gboolean is_sandboxed = FALSE;
-
-      if (!xdp_is_flatpak (pid, &is_sandboxed, error))
-        return NULL;
-
-      if (is_sandboxed)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Can't manually register a flatpak application");
-          return NULL;
-        }
+      g_autoptr(XdpAppInfo) detected_app_info = NULL;
 
-      if (!xdp_is_snap (pid, &is_sandboxed, error))
+      detected_app_info = xdp_connection_create_app_info_sync (connection,
+                                                               sender,
+                                                               cancellable,
+                                                               error);
+      if (!detected_app_info)
         return NULL;
 
-      if (is_sandboxed)
+      if (!xdp_app_info_is_host (detected_app_info))
         {
           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Can't manually register a snap application");
+                       "Can't manually register non-host applications");
           return NULL;
         }
 

What's the problem with this? It exactly tells the reader the condition under which it is not allowed while also abstracting away any engine specifics.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO that is not better. It creates something based on some logic inside create_app_info_sync() which needs to become a bit more complicated, then checks if it did the right thing, instead of checking the condition, then explicitly creating the right thing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but you're creating more code to make this piece here more error prone than it needs to be.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a different opinions here I guess, you say "create dummy objects" which I found awkward because one use API in a way it isn't intended to, and I say "don't create dummy objects" which you find awkward because things become a bit more open coded.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a dummy object, it's the XdpAppInfo that results from auto-detection and you ask it the question you actually want an answer to which is "is this connection from a host" instead of "is this connection a flatpak or snap".

@@ -187,6 +188,33 @@ export_portal_implementation (GDBusConnection *connection,
g_debug ("providing portal %s", g_dbus_interface_skeleton_get_info (skeleton)->name);
}

static void
export_host_portal_implementation (GDBusConnection *connection,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit unfortunate that this function doesn't by itself make sure that only host apps can call interfaces exported by it. The burden is currently completely on the implementation.

Comment on lines +809 to +810
if (app_info)
app_info_kind = "flatpak";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (app_info)
app_info_kind = "flatpak";
app_info_kind = "flatpak";

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating app_info might have failed, and don't want to set it if it did, even if it'd be overwritten eventually.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other ones do exactly that though, so this isn't consistent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What other ones do you mean?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app_info_kind = "snap"; and app_info_kind = "derived host"; below

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, indeed. The intention was to make those who conditionally work conditionally set it, but seems I did the wrong thing for the snap case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the point is that none of the ifs here do any work and are just noise

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, not conditioning them ends up with intermediate incorrect state, which I found misleading.

@A6GibKm
Copy link
Contributor

A6GibKm commented Jan 14, 2025

Would it make sense to do this in GApplication (in addition to libportal/ashpd) if the application-id property is set?

@jadahl jadahl force-pushed the wip/host-portal-registry branch 2 times, most recently from 77ab91f to bd47696 Compare January 15, 2025 11:43
@jadahl
Copy link
Collaborator Author

jadahl commented Jan 15, 2025

Would it make sense to do this in GApplication (in addition to libportal/ashpd) if the application-id property is set?

Perhaps, I wonder if it should be possible to calling Register() multiple times with the same application ID to make this easier, or whether apps should know whether Q/GApplication does it for them. Pushed a branch that allows that here: https://github.com/jadahl/xdg-desktop-portal/commits/wip/redundant-register

@jadahl jadahl force-pushed the wip/host-portal-registry branch from bd47696 to 35da526 Compare January 15, 2025 14:02
@jadahl jadahl force-pushed the wip/host-portal-registry branch from a4e68c4 to 291e77c Compare January 15, 2025 21:48
@jadahl jadahl force-pushed the wip/host-portal-registry branch from 5633f7f to 5bb5595 Compare January 27, 2025 13:59
@jadahl
Copy link
Collaborator Author

jadahl commented Jan 27, 2025

I have no opinion on error vs success on registering with the same ID as well.

@GeorgesStavracas
Copy link
Member

@swick
Copy link
Collaborator

swick commented Jan 27, 2025

I have no opinion on error vs success on registering with the same ID as well.

Alright, let's go with the current implementation then. We could relax this later on.

@GeorgesStavracas GeorgesStavracas added this to the 1.20 milestone Jan 27, 2025
@GeorgesStavracas GeorgesStavracas added the portal: host registry Host registry portal label Jan 27, 2025
@GeorgesStavracas GeorgesStavracas added this pull request to the merge queue Jan 27, 2025
Merged via the queue into flatpak:main with commit 8289226 Jan 27, 2025
5 checks passed
@mcatanzaro
Copy link
Contributor

What happens if xdg-desktop-portal isn't running yet when the application attempts to register itself, or if xdg-desktop-portarl crashes, or if it gets manually restarted by running xdg-desktop-portal -r? I think it is going to forget about apps that have previously registered themselves, right? Will we wind up with apps sometimes falling back to permissions for the empty string app instead of using the permissions for its app ID?

@pwithnall
Copy link
Contributor

pwithnall commented Jan 28, 2025

This looks a lot like it duplicates the session management protocol: GtkApplication already calls RegisterClient with the application’s app ID here.

It would be great if we could avoid another sync call in the startup path for every application, given that information (app ID, app’s D-Bus connection name) is already available to the session manager.

Could the portal query this information from the session manager when it’s needed (potentially with a bulk query too, allowing for O(1) calls rather than O(N) in the number of apps)?

Apologies if this has been covered already or is inapplicable for some reason, I only skim-read the many comments above.

@ebassi
Copy link
Collaborator

ebassi commented Jan 28, 2025

This looks a lot like it duplicates the session management protocol: GtkApplication already calls RegisterClient with the application’s app ID here.

There is no session management shared interface: it's only available on GNOME and Xfce. We might end up with a Wayland protocol for session management, but right now it's still an open issue if the session object ought to be tied to an application id.

Portals are supposed to work in multiple environments, and not all of them have a session manager.

@jadahl
Copy link
Collaborator Author

jadahl commented Jan 28, 2025

I think it is going to forget about apps that have previously registered themselves, right?

They indeed need to re-register, ideally by listening to the portal name coming back. Something libportal ideally should handle itself.

It would be great if we could avoid another sync call in the startup path for every application, given that information (app ID, app’s D-Bus connection name) is already available to the session manager.

It doesn't need to be sync, does it? The app can continue the same no matter if the call succeeded or not.

@mcatanzaro
Copy link
Contributor

They indeed need to re-register, ideally by listening to the portal name coming back. Something libportal ideally should handle itself.

OK, that should work, but it should be documented more clearly. Plenty of applications talk to portals directly without using libportal, and developers won't know to implement this manually if we don't draw some attention to it.

@matthiasclasen
Copy link
Contributor

Synching up between session manager and portals about some newly appeared application sounds difficult at best

@pwithnall
Copy link
Contributor

It would be great if we could avoid another sync call in the startup path for every application, given that information (app ID, app’s D-Bus connection name) is already available to the session manager.

Georges asked me to comment further on this, and I think I’ve settled my thoughts. So:

  • I still believe this should be implemented by gnome-session on GNOME/Xfce, i.e. via xdg-desktop-portal-gnome interfacing with gnome-session.
  • On other platforms, something like this host portal registry will be needed.
  • In particular, gnome-session continues to be needed for non-flatpak apps, and it’s what we’re (still, slowly) hanging session save/restore support off, so it’s not going to go away any time soon.
  • It already has a mapping between app and D-Bus connection.
  • It seems really unfortunate that this feature has got so far into development without gnome-session being explored. That leads us to a sunk cost situation where nobody wins. I’m not a xdg-desktop-portal developer so I don’t really have any sway here.
  • However, I will not merge changes into GLib which make a second session registration call in GApplication.
  • Running two parallel session management protocols sounds like a great way to end up in corner case situations (what if one of them times out, or one succeeds but the other one fails, why should we pay the cost of two D-Bus calls on every application startup)
  • So if an implementation for this protocol is to land in GLib, it will either need to replace the gnome-session protocol (I don’t see how that’s possible), or only be used if the gnome-session protocol is not used

@Sodivad
Copy link
Contributor

Sodivad commented Feb 11, 2025

I still believe this should be implemented by gnome-session on GNOME/Xfce, i.e. via xdg-desktop-portal-gnome interfacing with gnome-session.
On other platforms, something like this host portal registry will be needed.

So we need this after all.

It seems really unfortunate that this feature has got so far into development without gnome-session being explored. That leads us to a sunk cost situation where nobody wins. I’m not a xdg-desktop-portal developer so I don’t really have any sway here

xdg-desktop-portal is desktop agnostic why should it explore an interface that only gnome implements.
And as you said other platforms would need this any way.

However, I will not merge changes into GLib which make a second session registration call in GApplication.
Running two parallel session management protocols sounds like a great way to end up in corner case situations (what if one of them times out, or one succeeds but the other one fails, why should we pay the cost of two D-Bus calls on every application startup)

This is not a session managment protocol.

If you worry about sync dbus calls on startup, it doesn't need to by sync and can be async just fine (that's what my Qt impl does)

You also fail to consider non-GLib apps running on Gnome, which do not use the gnome session managment.

@pwithnall
Copy link
Contributor

pwithnall commented Feb 11, 2025

I still believe this should be implemented by gnome-session on GNOME/Xfce, i.e. via xdg-desktop-portal-gnome interfacing with gnome-session.
On other platforms, something like this host portal registry will be needed.

So we need this after all.

Yes, I said that.

It seems really unfortunate that this feature has got so far into development without gnome-session being explored. That leads us to a sunk cost situation where nobody wins. I’m not a xdg-desktop-portal developer so I don’t really have any sway here

xdg-desktop-portal is desktop agnostic why should it explore an interface that only gnome implements. And as you said other platforms would need this any way.

xdg-desktop-portal-gnome is not desktop agnostic. aiui the portal APIs have always been designed to allow for different desktop-specific implementations.

However, I will not merge changes into GLib which make a second session registration call in GApplication.
Running two parallel session management protocols sounds like a great way to end up in corner case situations (what if one of them times out, or one succeeds but the other one fails, why should we pay the cost of two D-Bus calls on every application startup)

This is not a session managment protocol.

aiui it’s about associating an app’s D-Bus session bus connection with the app ID and cgroup. That counts as session management, in my book, even if it doesn’t do all the same things as other session management protocols.

If you worry about sync dbus calls on startup, it doesn't need to by sync and can be async just fine (that's what my Qt impl does)

Perhaps I shouldn’t have emphasised sync. Sync calls on startup are terrible, but async calls still raise all the questions of what to do if they fail, time out, etc. IPC is not simple, doing less of it is always better.

@swick
Copy link
Collaborator

swick commented Feb 11, 2025

aiui it’s about associating an app’s D-Bus session bus connection with the app ID and cgroup. That counts as session management, in my book, even if it doesn’t do all the same things as other session management protocols.

I don't see what the session is here? The dbus connection?

Perhaps I shouldn’t have emphasised sync. Sync calls on startup are terrible, but async calls still raise all the questions of what to do if they fail, time out, etc. IPC is not simple, doing less of it is always better.

Nothing. This whole registry is band aid.

xdg-desktop-portal-gnome is not desktop agnostic. aiui the portal APIs have always been designed to allow for different desktop-specific implementations.

This is not a portal though, and the flow is backwards: the client, not some backend, provides this information and that's the entire point of it.

@A6GibKm
Copy link
Contributor

A6GibKm commented Feb 11, 2025

Nothing. This whole registry is band aid.

I think the bigger issue would be potential races. In snapshots we call the camera portal asap and this is yet another thing that might be not be done before we need the app id.

@swick
Copy link
Collaborator

swick commented Feb 11, 2025

Then you can call it yourself and let the gtk/glib one potentially fail.

@grulja
Copy link
Contributor

grulja commented Feb 11, 2025

Nothing. This whole registry is band aid.

I think the bigger issue would be potential races. In snapshots we call the camera portal asap and this is yet another thing that might be not be done before we need the app id.

I already have MRs for Firefox and Chromium, both also call the settings portal to get color scheme right early in the initialization and I was still able to make it work. I believe every app should be able to find a way to make a proper use of this API.

@swick
Copy link
Collaborator

swick commented Feb 11, 2025

It bears repeating: this is band-aid. It's a tool for apps to work around the system not putting them into an appropriate cgroup.

I honestly don't care if we put the band-aid into gtk/glib or not. What we really should put there is some code that makes sure we spawn apps into the right cgroup.

gnomesysadmins pushed a commit to GNOME/gtk that referenced this pull request Feb 15, 2025
When not under a sandbox, register the app with the new host app
registry in XDG Desktop Portal. This allows host system apps be
better recognized, and behave a little better, when interacting
with portals.

This has to happen very early during initalization, before even
the gtk_init() call, because e.g. on Wayland the Settings portal
is used when opening the display.

See flatpak/xdg-desktop-portal#1521
@mcatanzaro
Copy link
Contributor

My concern with the registry is it's being presented as a temporary band-aid solution, which doesn't make sense to me at all since creating a new D-Bus API seems like a very permanent solution.

But I have no objection to just keeping this registry API around forever. It works and it solves the problem, right? Why not?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
portal: host registry Host registry portal
Projects
No open projects
Status: Needs Triage
Development

Successfully merging this pull request may close these issues.