From 7c15bc6b38ab9567630f666ebba22cdfc668e427 Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 17:31:39 +0300 Subject: [PATCH 1/6] Launch applications inside a Systemd scope --- meson.build | 2 ++ src/AppSystem/App.vala | 80 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/meson.build b/meson.build index 23d1c0d3..b471f4fc 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,7 @@ glib_version = '2.74' gio_dep = dependency('gio-2.0', version: '>=@0@'.format(glib_version)) gio_unix_dep = dependency('gio-unix-2.0', version: '>=@0@'.format(glib_version)) glib_dep = dependency('glib-2.0', version: '>=@0@'.format(glib_version)) +gnome_desktop_dep = dependency('gnome-desktop-4') gobject_dep = dependency('gobject-2.0', version: '>=@0@'.format(glib_version)) gtk_dep = dependency('gtk4') gtk_wayland_dep = dependency('gtk4-wayland') @@ -40,6 +41,7 @@ dependencies = [ gio_dep, gio_unix_dep, glib_dep, + gnome_desktop_dep, gobject_dep, gtk_dep, gtk_wayland_dep, diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index 5c1f8e8d..7a075b17 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -137,6 +137,8 @@ public class Dock.App : Object { switcheroo_control.apply_gpu_environment (context, prefers_nondefault_gpu); } + context.launched.connect (start_systemd_scope); + try { if (action != null) { app_info.launch_action (action, context); @@ -152,6 +154,84 @@ public class Dock.App : Object { } } + private void start_systemd_scope (AppLaunchContext context, GLib.AppInfo appinfo, Variant platform_data) { + var dbus_activatable = appinfo is DesktopAppInfo && ((DesktopAppInfo) appinfo).get_boolean ("DBusActivatable"); + + int pid; + if (!platform_data.lookup ("pid", "i", out pid)) { + return; + } + + var app_name = appinfo.get_id () ?? appinfo.get_executable (); + // if we got a path, use the binary name only + if (app_name.has_prefix ("/")) { + app_name = Path.get_basename (app_name); + } else if (app_name.has_suffix (".desktop")) { + app_name = app_name.substring (0, app_name.length - 8); + } + + DBusConnection connection; + try { + connection = Bus.get_sync (SESSION, null); + } catch (Error e) { + critical ("Couldn't connect to DBus: %s", e.message); + return; + } + + // If an app is dbus activatable, we don't launch it directly + // dbus-daemon launches it for us, so we can't get pid from platform_data + if (dbus_activatable) { + try { + var reply = connection.call_sync ( + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID", + new Variant ("(s)", app_name), + new VariantType ("(u)"), + NONE, + -1, + null + ); + + reply.get ("(u)", out pid); + } catch (Error e) { + critical ("Couldn't get pid of dbus activatable app: %s", e.message); + return; + } + } + + var builder = new VariantBuilder (new VariantType ("(ssa(sv)a(sa(sv)))")); + builder.add ("s", "app-pantheon-%s-%d.scope".printf (app_name, pid)); + builder.add ("s", "fail"); + + builder.open (new VariantType ("a(sv)")); + builder.add ("(sv)", "PIDs", new Variant.array (VariantType.UINT32, { (uint32) pid })); + + // Default to let systemd garbage collect failed applications we launched. + builder.add ("(sv)", "CollectMode", new Variant.string ("inactive-or-failed")); + builder.close (); + + builder.open (new VariantType ("a(sa(sv))")); + builder.close (); + + try { + connection.call_sync ( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit", + builder.end (), + new VariantType ("(o)"), + NO_AUTO_START, + 1000, + null + ); + } catch (Error e) { + warning ("Couldn't put an app into Systemd scope: %s", e.message); + } + } + public bool launch_new_instance (AppLaunchContext context) { // Treat this as a string to distinguish between false and null var single_main_window = app_info.get_string ("SingleMainWindow"); From ff310dc14221f6e423bcf48782fd2aac953f9e9b Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 17:32:18 +0300 Subject: [PATCH 2/6] Remove gnome-desktop dependency --- meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/meson.build b/meson.build index b471f4fc..23d1c0d3 100644 --- a/meson.build +++ b/meson.build @@ -24,7 +24,6 @@ glib_version = '2.74' gio_dep = dependency('gio-2.0', version: '>=@0@'.format(glib_version)) gio_unix_dep = dependency('gio-unix-2.0', version: '>=@0@'.format(glib_version)) glib_dep = dependency('glib-2.0', version: '>=@0@'.format(glib_version)) -gnome_desktop_dep = dependency('gnome-desktop-4') gobject_dep = dependency('gobject-2.0', version: '>=@0@'.format(glib_version)) gtk_dep = dependency('gtk4') gtk_wayland_dep = dependency('gtk4-wayland') @@ -41,7 +40,6 @@ dependencies = [ gio_dep, gio_unix_dep, glib_dep, - gnome_desktop_dep, gobject_dep, gtk_dep, gtk_wayland_dep, From baa9ff9ca9d7f409994145c16b46f3d4b05e5003 Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 17:34:28 +0300 Subject: [PATCH 3/6] Start Systemd scope asynchronously --- src/AppSystem/App.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index 7a075b17..2667ec22 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -154,7 +154,7 @@ public class Dock.App : Object { } } - private void start_systemd_scope (AppLaunchContext context, GLib.AppInfo appinfo, Variant platform_data) { + private async void start_systemd_scope (AppLaunchContext context, GLib.AppInfo appinfo, Variant platform_data) { var dbus_activatable = appinfo is DesktopAppInfo && ((DesktopAppInfo) appinfo).get_boolean ("DBusActivatable"); int pid; @@ -190,7 +190,7 @@ public class Dock.App : Object { new Variant ("(s)", app_name), new VariantType ("(u)"), NONE, - -1, + 1000, null ); From 26ecd00b07abf2b3d4880f0affaa9a832084309d Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 17:37:05 +0300 Subject: [PATCH 4/6] Add scope description --- src/AppSystem/App.vala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index 2667ec22..cd32d70d 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -206,6 +206,7 @@ public class Dock.App : Object { builder.add ("s", "fail"); builder.open (new VariantType ("a(sv)")); + builder.add ("(sv)", "Description", new Variant.string ("Application launched by %s".printf (GLib.Application.get_default ().application_id))); builder.add ("(sv)", "PIDs", new Variant.array (VariantType.UINT32, { (uint32) pid })); // Default to let systemd garbage collect failed applications we launched. From e73aab27cea598161e9bbb2ab4433672b2403a6e Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 17:49:20 +0300 Subject: [PATCH 5/6] Simplify --- src/AppSystem/App.vala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index cd32d70d..806ed060 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -155,8 +155,6 @@ public class Dock.App : Object { } private async void start_systemd_scope (AppLaunchContext context, GLib.AppInfo appinfo, Variant platform_data) { - var dbus_activatable = appinfo is DesktopAppInfo && ((DesktopAppInfo) appinfo).get_boolean ("DBusActivatable"); - int pid; if (!platform_data.lookup ("pid", "i", out pid)) { return; @@ -180,7 +178,7 @@ public class Dock.App : Object { // If an app is dbus activatable, we don't launch it directly // dbus-daemon launches it for us, so we can't get pid from platform_data - if (dbus_activatable) { + if (appinfo is DesktopAppInfo && ((DesktopAppInfo) appinfo).get_boolean ("DBusActivatable")) { try { var reply = connection.call_sync ( "org.freedesktop.DBus", From 5779c0940265810f4b705e3e77d7499a8de3b2fe Mon Sep 17 00:00:00 2001 From: lenemter Date: Thu, 18 Sep 2025 18:26:09 +0300 Subject: [PATCH 6/6] Escape app name when sending to systemd --- src/AppSystem/App.vala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index 806ed060..7bf38fc3 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -199,8 +199,18 @@ public class Dock.App : Object { } } + var string_builder = new StringBuilder.sized (app_name.length); + for (var i = 0; i < app_name.length; i++) { + var c = app_name[i]; + if (c.isalnum () || c == ':' || c == '_' || c == '.') { + string_builder.append_c (c); + } else { + string_builder.append_printf ("\\x%02x", c); + } + } + var builder = new VariantBuilder (new VariantType ("(ssa(sv)a(sa(sv)))")); - builder.add ("s", "app-pantheon-%s-%d.scope".printf (app_name, pid)); + builder.add ("s", "app-pantheon-%s-%d.scope".printf (string_builder.free_and_steal (), pid)); builder.add ("s", "fail"); builder.open (new VariantType ("a(sv)"));