From b5049cf1826f7621dbee72b349e1e342c446c312 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Thu, 24 Jun 2021 17:26:08 +0100
Subject: [PATCH] pv-wrap: Optionally try to run games in a systemd scope

This neatly separates the processes for the game (which can be
numerous, for example for Proton games that need to run wineserver and
winedevice.exe) from the processes for Steam.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/flatpak-run-private.h |  6 ++++
 pressure-vessel/flatpak-run.c         | 18 ++++++++---
 pressure-vessel/wrap-setup.c          | 43 ++++++++++++++++++++++++++
 pressure-vessel/wrap-setup.h          |  4 +++
 pressure-vessel/wrap.c                | 44 ++++++++++++++-------------
 5 files changed, 90 insertions(+), 25 deletions(-)

diff --git a/pressure-vessel/flatpak-run-private.h b/pressure-vessel/flatpak-run-private.h
index 67b5cc0ad..f128f00c3 100644
--- a/pressure-vessel/flatpak-run-private.h
+++ b/pressure-vessel/flatpak-run-private.h
@@ -38,6 +38,12 @@
 #include "flatpak-utils-private.h"
 #include "steam-runtime-tools/glib-backports-internal.h"
 
+/* modified for pressure-vessel */
+gboolean flatpak_run_in_transient_unit (const char *owner,
+                                        const char *prefix,
+                                        const char *appid,
+                                        GError    **error);
+
 /* See flatpak-metadata(5) */
 
 #define FLATPAK_METADATA_GROUP_APPLICATION "Application"
diff --git a/pressure-vessel/flatpak-run.c b/pressure-vessel/flatpak-run.c
index a43babb7b..32b07ae19 100644
--- a/pressure-vessel/flatpak-run.c
+++ b/pressure-vessel/flatpak-run.c
@@ -48,6 +48,7 @@
 
 #include "flatpak-utils-base-private.h"
 #include "flatpak-utils-private.h"
+#include "flatpak-systemd-dbus-generated.h"
 
 /* In Flatpak this is optional, in pressure-vessel not so much */
 #define ENABLE_XAUTH
@@ -1673,6 +1674,8 @@ flatpak_ensure_data_dir (GFile        *app_id_dir,
   return TRUE;
 }
 
+#endif
+
 struct JobData
 {
   char      *job;
@@ -1707,8 +1710,16 @@ systemd_unit_name_escape (const gchar *in)
   return g_string_free (str, FALSE);
 }
 
+/*
+ * Modified from Flatpak's version. For simplicity we assume that @owner
+ * and @prefix are in a form that don't need escaping to be a valid systemd
+ * scope name.
+ */
 gboolean
-flatpak_run_in_transient_unit (const char *appid, GError **error)
+flatpak_run_in_transient_unit (const char *owner,
+                               const char *prefix,
+                               const char *appid,
+                               GError **error)
 {
   g_autoptr(GDBusConnection) conn = NULL;
   g_autofree char *path = NULL;
@@ -1753,7 +1764,8 @@ flatpak_run_in_transient_unit (const char *appid, GError **error)
     goto out;
 
   appid_escaped = systemd_unit_name_escape (appid);
-  name = g_strdup_printf ("app-flatpak-%s-%d.scope", appid_escaped, getpid ());
+  name = g_strdup_printf ("app-%s-%s%s-%d.scope",
+                          owner, prefix, appid_escaped, getpid ());
 
   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sv)"));
 
@@ -1795,8 +1807,6 @@ out:
   return res;
 }
 
-#endif
-
 void
 flatpak_run_add_font_path_args (FlatpakBwrap *bwrap)
 {
diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c
index 98f48ab05..b381bdbe8 100644
--- a/pressure-vessel/wrap-setup.c
+++ b/pressure-vessel/wrap-setup.c
@@ -287,3 +287,46 @@ pv_wrap_use_host_os (FlatpakExports *exports,
 
   return TRUE;
 }
+
+const char *
+pv_wrap_get_steam_app_id (const char *from_command_line)
+{
+  const char *value;
+
+  if (from_command_line != NULL)
+    return from_command_line;
+
+  if ((value = g_getenv ("STEAM_COMPAT_APP_ID")) != NULL)
+    return value;
+
+  if ((value = g_getenv ("SteamAppId")) != NULL)
+    return value;
+
+  return NULL;
+}
+
+/*
+ * Try to move the current process into a scope defined by the given
+ * Steam app ID. If that's not possible, ignore.
+ */
+void
+pv_wrap_move_into_scope (const char *steam_app_id)
+{
+  g_autoptr(GError) local_error = NULL;
+
+  if (steam_app_id != NULL)
+    {
+      if (steam_app_id[0] == '\0')
+        steam_app_id = NULL;
+      else if (strcmp (steam_app_id, "0") == 0)
+        steam_app_id = NULL;
+    }
+
+  if (steam_app_id != NULL)
+    flatpak_run_in_transient_unit ("steam", "app", steam_app_id, &local_error);
+  else
+    flatpak_run_in_transient_unit ("steam", "", "unknown", &local_error);
+
+  if (local_error != NULL)
+    g_debug ("Cannot move into a systemd scope: %s", local_error->message);
+}
diff --git a/pressure-vessel/wrap-setup.h b/pressure-vessel/wrap-setup.h
index aae933076..cfeef954a 100644
--- a/pressure-vessel/wrap-setup.h
+++ b/pressure-vessel/wrap-setup.h
@@ -36,3 +36,7 @@ void pv_wrap_share_sockets (FlatpakBwrap *bwrap,
 gboolean pv_wrap_use_host_os (FlatpakExports *exports,
                               FlatpakBwrap *bwrap,
                               GError **error);
+
+void pv_wrap_move_into_scope (const char *steam_app_id);
+
+const char *pv_wrap_get_steam_app_id (const char *from_command_line);
diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c
index 2bcb3d5df..d2b352f0d 100644
--- a/pressure-vessel/wrap.c
+++ b/pressure-vessel/wrap.c
@@ -703,6 +703,7 @@ static char *opt_runtime_id = NULL;
 static Tristate opt_share_home = TRISTATE_MAYBE;
 static gboolean opt_share_pid = TRUE;
 static gboolean opt_single_thread = FALSE;
+static gboolean opt_systemd_scope = FALSE;
 static double opt_terminate_idle_timeout = 0.0;
 static double opt_terminate_timeout = -1.0;
 static char *opt_variable_dir = NULL;
@@ -1222,6 +1223,12 @@ static GOptionEntry options[] =
     G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_single_thread,
     "Disable multi-threaded code paths, for debugging",
     NULL },
+  { "systemd-scope", '\0',
+    G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_systemd_scope,
+    "Attempt to run the game in a systemd scope", NULL },
+  { "no-systemd-scope", '\0',
+    G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_systemd_scope,
+    "Do not run the game in a systemd scope", NULL },
   { "terminal", '\0',
     G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, opt_terminal_cb,
     "none: disable features that would use a terminal; "
@@ -1338,6 +1345,7 @@ main (int argc,
   g_autoptr(PvRuntime) runtime = NULL;
   g_autoptr(FILE) original_stdout = NULL;
   g_autoptr(GArray) pass_fds_through_adverb = g_array_new (FALSE, FALSE, sizeof (int));
+  const char *steam_app_id;
 
   setlocale (LC_ALL, "");
 
@@ -1399,6 +1407,8 @@ main (int argc,
 
   opt_remove_game_overlay = pv_boolean_environment ("PRESSURE_VESSEL_REMOVE_GAME_OVERLAY",
                                                     FALSE);
+  opt_systemd_scope = pv_boolean_environment ("PRESSURE_VESSEL_SYSTEMD_SCOPE",
+                                              opt_systemd_scope);
   opt_import_vulkan_layers = pv_boolean_environment ("PRESSURE_VESSEL_IMPORT_VULKAN_LAYERS",
                                                      TRUE);
 
@@ -1577,6 +1587,8 @@ main (int argc,
       argc--;
     }
 
+  steam_app_id = pv_wrap_get_steam_app_id (opt_steam_app_id);
+
   home = g_get_home_dir ();
 
   if (opt_share_home == TRISTATE_YES)
@@ -1598,27 +1610,11 @@ main (int argc,
       opt_fake_home = g_build_filename (home, ".var", "app",
                                         opt_freedesktop_app_id, NULL);
     }
-  else if (opt_steam_app_id)
-    {
-      is_home_shared = FALSE;
-      opt_freedesktop_app_id = g_strdup_printf ("com.steampowered.App%s",
-                                                opt_steam_app_id);
-      opt_fake_home = g_build_filename (home, ".var", "app",
-                                        opt_freedesktop_app_id, NULL);
-    }
-  else if (g_getenv ("STEAM_COMPAT_APP_ID") != NULL)
-    {
-      is_home_shared = FALSE;
-      opt_freedesktop_app_id = g_strdup_printf ("com.steampowered.App%s",
-                                                g_getenv ("STEAM_COMPAT_APP_ID"));
-      opt_fake_home = g_build_filename (home, ".var", "app",
-                                        opt_freedesktop_app_id, NULL);
-    }
-  else if (g_getenv ("SteamAppId") != NULL)
+  else if (steam_app_id != NULL)
     {
       is_home_shared = FALSE;
       opt_freedesktop_app_id = g_strdup_printf ("com.steampowered.App%s",
-                                                g_getenv ("SteamAppId"));
+                                                steam_app_id);
       opt_fake_home = g_build_filename (home, ".var", "app",
                                         opt_freedesktop_app_id, NULL);
     }
@@ -2787,9 +2783,15 @@ main (int argc,
     }
 
   if (opt_only_prepare)
-    ret = 0;
-  else
-    pv_bwrap_execve (final_argv, fileno (original_stdout), error);
+    {
+      ret = 0;
+      goto out;
+    }
+
+  if (opt_systemd_scope)
+    pv_wrap_move_into_scope (steam_app_id);
+
+  pv_bwrap_execve (final_argv, fileno (original_stdout), error);
 
 out:
   if (local_error != NULL)
-- 
GitLab