4073ae
From 5e42384cc4499293259a8a37a737014a56de34df Mon Sep 17 00:00:00 2001
4073ae
From: Benjamin Berg <bberg@redhat.com>
4073ae
Date: Fri, 23 Oct 2020 18:20:01 +0200
4073ae
Subject: [PATCH 1/4] tests: Iterate mainloop during launch test
4073ae
4073ae
When launching an application, we wait for the DBus response from
4073ae
systemd before executing the binary. Because of this the main loop needs
4073ae
to be iterated for spawning to completed and the file to be created.
4073ae
4073ae
Without this the test will time out if GLib was able to connect to the
4073ae
session bus.
4073ae
---
4073ae
 gio/tests/desktop-app-info.c | 1 +
4073ae
 1 file changed, 1 insertion(+)
4073ae
4073ae
diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c
4073ae
index fcc29c579..743230cbb 100644
4073ae
--- a/gio/tests/desktop-app-info.c
4073ae
+++ b/gio/tests/desktop-app-info.c
4073ae
@@ -334,6 +334,7 @@ wait_for_file (const gchar *want_this,
4073ae
    */
4073ae
   while (access (want_this, F_OK) != 0)
4073ae
     {
4073ae
+      g_main_context_iteration (NULL, FALSE);
4073ae
       g_usleep (100000); /* 100ms */
4073ae
       g_assert_cmpuint (retries, >, 0);
4073ae
       retries--;
4073ae
-- 
4073ae
2.31.1
4073ae
4073ae
From ba3b85a8fea0151e74de50e841a7f16f9b077a56 Mon Sep 17 00:00:00 2001
4073ae
From: Benjamin Berg <bberg@redhat.com>
4073ae
Date: Mon, 27 Jul 2020 22:22:32 +0200
4073ae
Subject: [PATCH 2/4] gdesktopappinfo: Move launched applications into
4073ae
 transient scope
4073ae
4073ae
Try to move the spawned executable into its own systemd scope. To avoid
4073ae
possible race conditions and ensure proper accounting, we delay the
4073ae
execution of the real command until after the DBus call to systemd has
4073ae
finished.
4073ae
4073ae
From the two approaches we can take here, this is better in the sense
4073ae
that we have a child that the API consumer can watch. API consumers
4073ae
should not be doing this, however, gnome-session needs to watch children
4073ae
during session startup. Until gnome-session is fixed, we will not be
4073ae
able to change this.
4073ae
4073ae
The alternative approach is to delegate launching itself to systemd by
4073ae
creating a transient .service unit instead. This is cleaner and has e.g.
4073ae
the advantage that systemd will take care of log redirection and similar
4073ae
issues.
4073ae
4073ae
Note that this patch is incomplete. The DBus call is done in a "fire and
4073ae
forget" manner, which is fine in most cases, but means that "gio open"
4073ae
will fail to move the child into the new scope as gio quits before the
4073ae
DBus call finishes.
4073ae
---
4073ae
 gio/gdesktopappinfo.c | 264 ++++++++++++++++++++++++++++++++++++------
4073ae
 1 file changed, 227 insertions(+), 37 deletions(-)
4073ae
4073ae
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
4073ae
index 1a4b97918..afdcd42ac 100644
4073ae
--- a/gio/gdesktopappinfo.c
4073ae
+++ b/gio/gdesktopappinfo.c
4073ae
@@ -2730,6 +2730,148 @@ notify_desktop_launch (GDBusConnection  *session_bus,
4073ae
 
4073ae
 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
4073ae
 
4073ae
+#if defined(__linux__) && !defined(__BIONIC__)
4073ae
+typedef struct {
4073ae
+  int pipe[2];
4073ae
+  GSpawnChildSetupFunc user_setup;
4073ae
+  gpointer             user_setup_data;
4073ae
+} SpawnWrapperData;
4073ae
+
4073ae
+static void
4073ae
+launch_uris_with_spawn_delay_exec (gpointer user_data)
4073ae
+{
4073ae
+  SpawnWrapperData *data = user_data;
4073ae
+
4073ae
+  /* Clear CLOEXEC again, as that was set due to
4073ae
+   * G_SPAWN_LEAVE_DESCRIPTORS_OPEN not being set. */
4073ae
+  fcntl (data->pipe[0], F_SETFD, 0);
4073ae
+
4073ae
+  /* No need to close read side, we have CLOEXEC set. */
4073ae
+
4073ae
+  if (data->user_setup)
4073ae
+    data->user_setup (data->user_setup_data);
4073ae
+}
4073ae
+
4073ae
+static gchar *
4073ae
+systemd_unit_name_escape (const gchar *in)
4073ae
+{
4073ae
+  /* Adapted from systemd source */
4073ae
+  GString * const str = g_string_sized_new (strlen (in));
4073ae
+
4073ae
+  for (; *in; in++)
4073ae
+    {
4073ae
+      if (g_ascii_isalnum (*in) || *in == ':' || *in == '_' || *in == '.')
4073ae
+        g_string_append_c (str, *in);
4073ae
+      else
4073ae
+        g_string_append_printf (str, "\\x%02x", *in);
4073ae
+    }
4073ae
+  return g_string_free (str, FALSE);
4073ae
+}
4073ae
+
4073ae
+static void
4073ae
+create_systemd_scope (GDBusConnection    *session_bus,
4073ae
+                      GDesktopAppInfo    *info,
4073ae
+                      gint                pid,
4073ae
+                      GAsyncReadyCallback callback,
4073ae
+                      gpointer            user_data)
4073ae
+{
4073ae
+  GVariantBuilder builder;
4073ae
+  const char *app_name = g_get_application_name ();
4073ae
+  char *appid = NULL;
4073ae
+  char *appid_escaped = NULL;
4073ae
+  char *snid_escaped = NULL;
4073ae
+  char *unit_name = NULL;
4073ae
+
4073ae
+  /* In this order:
4073ae
+   *  1. Actual application ID from file
4073ae
+   *  2. Stripping the .desktop from the desktop ID
4073ae
+   *  3. Fall back to using the binary name
4073ae
+   */
4073ae
+  if (info->app_id)
4073ae
+    appid = g_strdup (info->app_id);
4073ae
+  else if (info->desktop_id && g_str_has_suffix (info->desktop_id, ".desktop"))
4073ae
+    appid = g_strndup (info->desktop_id, strlen (info->desktop_id) - 8);
4073ae
+  else
4073ae
+    appid = g_path_get_basename (info->binary);
4073ae
+
4073ae
+  appid_escaped = systemd_unit_name_escape (appid);
4073ae
+
4073ae
+  /* Generate a name conforming to
4073ae
+   *   https://systemd.io/DESKTOP_ENVIRONMENTS/
4073ae
+   * We use the PID to disambiguate, as that should be unique enough.
4073ae
+   */
4073ae
+  unit_name = g_strdup_printf ("app-glib-%s-%d.scope", appid_escaped, pid);
4073ae
+
4073ae
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ssa(sv)a(sa(sv)))"));
4073ae
+  g_variant_builder_add (&builder, "s", unit_name);
4073ae
+  g_variant_builder_add (&builder, "s", "fail");
4073ae
+
4073ae
+  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(sv)"));
4073ae
+
4073ae
+  /* Add a generic human readable description, can be changed at will. */
4073ae
+  if (app_name)
4073ae
+    g_variant_builder_add (&builder,
4073ae
+                           "(sv)",
4073ae
+                           "Description",
4073ae
+                           g_variant_new_take_string (g_strdup_printf ("Application launched by %s",
4073ae
+                                                                       app_name)));
4073ae
+  g_variant_builder_add (&builder,
4073ae
+                         "(sv)",
4073ae
+                         "PIDs",
4073ae
+                          g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, &pid, 1, 4));
4073ae
+  /* Default to let systemd garbage collect failed applications we launched. */
4073ae
+  g_variant_builder_add (&builder,
4073ae
+                         "(sv)",
4073ae
+                         "CollectMode",
4073ae
+                          g_variant_new_string ("inactive-or-failed"));
4073ae
+
4073ae
+  g_variant_builder_close (&builder);
4073ae
+
4073ae
+  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(sa(sv))"));
4073ae
+  g_variant_builder_close (&builder);
4073ae
+
4073ae
+  g_dbus_connection_call (session_bus,
4073ae
+                          "org.freedesktop.systemd1",
4073ae
+                          "/org/freedesktop/systemd1",
4073ae
+                          "org.freedesktop.systemd1.Manager",
4073ae
+                          "StartTransientUnit",
4073ae
+                          g_variant_builder_end (&builder),
4073ae
+                          G_VARIANT_TYPE ("(o)"),
4073ae
+                          G_DBUS_CALL_FLAGS_NO_AUTO_START,
4073ae
+                          1000,
4073ae
+                          NULL,
4073ae
+                          callback,
4073ae
+                          user_data);
4073ae
+
4073ae
+  g_free (appid);
4073ae
+  g_free (appid_escaped);
4073ae
+  g_free (snid_escaped);
4073ae
+  g_free (unit_name);
4073ae
+}
4073ae
+
4073ae
+static void
4073ae
+systemd_scope_created_cb (GObject      *object,
4073ae
+                          GAsyncResult *result,
4073ae
+                          gpointer      user_data)
4073ae
+{
4073ae
+  GVariant *res = NULL;
4073ae
+  GError *error = NULL;
4073ae
+
4073ae
+  res = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
4073ae
+  if (error != NULL)
4073ae
+    {
4073ae
+      g_debug ("Failed to move new child into scope: %s", error->message);
4073ae
+      g_error_free (error);
4073ae
+    }
4073ae
+
4073ae
+  /* Unblock the waiting wrapper binary. */
4073ae
+  close (GPOINTER_TO_INT (user_data));
4073ae
+
4073ae
+  if (res)
4073ae
+    g_variant_unref (res);
4073ae
+}
4073ae
+#endif
4073ae
+
4073ae
 static gboolean
4073ae
 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                                            GDBusConnection            *session_bus,
4073ae
@@ -2750,13 +2892,14 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
   GList *old_uris;
4073ae
   GList *dup_uris;
4073ae
 
4073ae
-  char **argv, **envp;
4073ae
+  GStrv argv = NULL, envp = NULL;
4073ae
+  GStrv wrapped_argv = NULL;
4073ae
+  GList *launched_uris = NULL;
4073ae
+  char *sn_id = NULL;
4073ae
   int argc;
4073ae
 
4073ae
   g_return_val_if_fail (info != NULL, FALSE);
4073ae
 
4073ae
-  argv = NULL;
4073ae
-
4073ae
   if (launch_context)
4073ae
     envp = g_app_launch_context_get_environment (launch_context);
4073ae
   else
4073ae
@@ -2770,27 +2913,19 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
   do
4073ae
     {
4073ae
       GPid pid;
4073ae
-      GList *launched_uris;
4073ae
       GList *iter;
4073ae
-      char *sn_id = NULL;
4073ae
-      char **wrapped_argv;
4073ae
       int i;
4073ae
-      gsize j;
4073ae
-      const gchar * const wrapper_argv[] =
4073ae
-        {
4073ae
-          "/bin/sh",
4073ae
-          "-e",
4073ae
-          "-u",
4073ae
-          "-c", "export GIO_LAUNCHED_DESKTOP_FILE_PID=$$; exec \"$@\"",
4073ae
-          "sh",  /* argv[0] for sh */
4073ae
-        };
4073ae
+#if defined(__linux__) && !defined(__BIONIC__)
4073ae
+      SpawnWrapperData wrapper_data;
4073ae
+#endif
4073ae
+      GSpawnChildSetupFunc setup = user_setup;
4073ae
+      gpointer             setup_data = user_setup_data;
4073ae
 
4073ae
       old_uris = dup_uris;
4073ae
       if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
4073ae
-        goto out;
4073ae
+        return FALSE;
4073ae
 
4073ae
       /* Get the subset of URIs we're launching with this process */
4073ae
-      launched_uris = NULL;
4073ae
       for (iter = old_uris; iter != NULL && iter != dup_uris; iter = iter->next)
4073ae
         launched_uris = g_list_prepend (launched_uris, iter->data);
4073ae
       launched_uris = g_list_reverse (launched_uris);
4073ae
@@ -2799,7 +2934,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
         {
4073ae
           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
                                _("Unable to find terminal required for application"));
4073ae
-          goto out;
4073ae
+          return FALSE;
4073ae
         }
4073ae
 
4073ae
       if (info->filename)
4073ae
@@ -2808,7 +2943,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                                  info->filename,
4073ae
                                  TRUE);
4073ae
 
4073ae
-      sn_id = NULL;
4073ae
       if (launch_context)
4073ae
         {
4073ae
           GList *launched_files = create_files_for_uris (launched_uris);
4073ae
@@ -2837,38 +2971,93 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
        * with a wrapper program (grep the GLib git history for
4073ae
        * `gio-launch-desktop` for an example of this which could be
4073ae
        * resurrected). */
4073ae
-      wrapped_argv = g_new (char *, argc + G_N_ELEMENTS (wrapper_argv) + 1);
4073ae
+      wrapped_argv = g_new (char *, argc + 6 + 1);
4073ae
+
4073ae
+      wrapped_argv[0] = g_strdup ("/bin/sh");
4073ae
+      wrapped_argv[1] = g_strdup ("-e");
4073ae
+      wrapped_argv[2] = g_strdup ("-u");
4073ae
+      wrapped_argv[3] = g_strdup ("-c");
4073ae
+      /* argument 4 is filled in below */
4073ae
+      wrapped_argv[5] = g_strdup ("sh");
4073ae
 
4073ae
-      for (j = 0; j < G_N_ELEMENTS (wrapper_argv); j++)
4073ae
-        wrapped_argv[j] = g_strdup (wrapper_argv[j]);
4073ae
       for (i = 0; i < argc; i++)
4073ae
-        wrapped_argv[i + G_N_ELEMENTS (wrapper_argv)] = g_steal_pointer (&argv[i]);
4073ae
+        wrapped_argv[i + 6] = g_steal_pointer (&argv[i]);
4073ae
+
4073ae
+      wrapped_argv[i + 6] = NULL;
4073ae
+      g_clear_pointer (&argv, g_free);
4073ae
+
4073ae
+#if defined(__linux__) && !defined(__BIONIC__)
4073ae
+      /* Create pipes, if we use a setup func, then set cloexec,
4073ae
+       * otherwise our wrapper script will close both sides. */
4073ae
+      if (!g_unix_open_pipe (wrapper_data.pipe, 0, NULL))
4073ae
+        {
4073ae
+          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
+                               _("Unable to create pipe for systemd synchronization"));
4073ae
+          return FALSE;
4073ae
+        }
4073ae
+
4073ae
+      /* Set CLOEXEC on the write pipe, so we don't need to deal with it in the child. */
4073ae
+      fcntl (wrapper_data.pipe[1], F_SETFD, FD_CLOEXEC);
4073ae
 
4073ae
-      wrapped_argv[i + G_N_ELEMENTS (wrapper_argv)] = NULL;
4073ae
-      g_free (argv);
4073ae
-      argv = NULL;
4073ae
+      if (!(spawn_flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
4073ae
+        {
4073ae
+          /* In this case, we use a setup function (which could probably also
4073ae
+           * overwrite envp to set GIO_LAUNCHED_DESKTOP_FILE_PID).
4073ae
+           *
4073ae
+           * Note that this does not incur an additional cost because
4073ae
+           * G_SPAWN_LEAVE_DESCRIPTOR_OPEN must be set in order to use
4073ae
+           * posix_spawn. */
4073ae
+          wrapper_data.user_setup = setup;
4073ae
+          wrapper_data.user_setup_data = setup_data;
4073ae
+
4073ae
+          setup = launch_uris_with_spawn_delay_exec;
4073ae
+          setup_data = &wrapper_data;
4073ae
+        }
4073ae
+
4073ae
+      wrapped_argv[4] = g_strdup_printf ("export GIO_LAUNCHED_DESKTOP_FILE_PID=$$; cat <&%1$d; exec \"$@\" %1$d<&-",
4073ae
+                                         wrapper_data.pipe[0]);
4073ae
+#else
4073ae
+      wrapped_argv[4] = g_strdup ("export GIO_LAUNCHED_DESKTOP_FILE_PID=$$; exec \"$@\"");
4073ae
+#endif
4073ae
 
4073ae
       if (!g_spawn_async_with_fds (info->path,
4073ae
                                    wrapped_argv,
4073ae
                                    envp,
4073ae
                                    spawn_flags,
4073ae
-                                   user_setup,
4073ae
-                                   user_setup_data,
4073ae
+                                   setup,
4073ae
+                                   setup_data,
4073ae
                                    &pid,
4073ae
                                    stdin_fd,
4073ae
                                    stdout_fd,
4073ae
                                    stderr_fd,
4073ae
                                    error))
4073ae
         {
4073ae
+#if defined(__linux__) && !defined(__BIONIC__)
4073ae
+          close (wrapper_data.pipe[0]);
4073ae
+          close (wrapper_data.pipe[1]);
4073ae
+#endif
4073ae
+
4073ae
           if (sn_id)
4073ae
             g_app_launch_context_launch_failed (launch_context, sn_id);
4073ae
 
4073ae
-          g_free (sn_id);
4073ae
-          g_list_free (launched_uris);
4073ae
-
4073ae
           goto out;
4073ae
         }
4073ae
 
4073ae
+#if defined(__linux__) && !defined(__BIONIC__)
4073ae
+      /* We close write side asynchronously later on when the dbus call
4073ae
+       * to systemd finishes. */
4073ae
+      close (wrapper_data.pipe[0]);
4073ae
+
4073ae
+      if (session_bus)
4073ae
+        create_systemd_scope (session_bus,
4073ae
+                              info,
4073ae
+                              pid,
4073ae
+                              systemd_scope_created_cb,
4073ae
+                              GINT_TO_POINTER (wrapper_data.pipe[1]));
4073ae
+      else
4073ae
+        close (wrapper_data.pipe[1]);
4073ae
+#endif
4073ae
+
4073ae
       if (pid_callback != NULL)
4073ae
         pid_callback (info, pid, pid_callback_data);
4073ae
 
4073ae
@@ -2893,19 +3082,20 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                              sn_id,
4073ae
                              launched_uris);
4073ae
 
4073ae
-      g_free (sn_id);
4073ae
-      g_list_free (launched_uris);
4073ae
-
4073ae
-      g_strfreev (wrapped_argv);
4073ae
-      wrapped_argv = NULL;
4073ae
+      g_clear_pointer (&sn_id, g_free);
4073ae
+      g_clear_pointer (&launched_uris, g_list_free);
4073ae
+      g_clear_pointer (&wrapped_argv, g_strfreev);
4073ae
     }
4073ae
   while (dup_uris != NULL);
4073ae
 
4073ae
   completed = TRUE;
4073ae
 
4073ae
- out:
4073ae
+out:
4073ae
   g_strfreev (argv);
4073ae
   g_strfreev (envp);
4073ae
+  g_clear_pointer (&wrapped_argv, g_strfreev);
4073ae
+  g_list_free (launched_uris);
4073ae
+  g_free (sn_id);
4073ae
 
4073ae
   return completed;
4073ae
 }
4073ae
-- 
4073ae
2.31.1
4073ae
4073ae
From cd67a1b0256d2397dac0836e154f3449b63a6b19 Mon Sep 17 00:00:00 2001
4073ae
From: Benjamin Berg <bberg@redhat.com>
4073ae
Date: Tue, 28 Jul 2020 12:11:13 +0200
4073ae
Subject: [PATCH 3/4] gdesktopappinfo: Handle task completion from spawn
4073ae
 function
4073ae
4073ae
This allows delaying the return of the task until all dbus calls (in
4073ae
particular the ones to setup the scope) have finished.
4073ae
4073ae
This fixes the behaviour of the previous commit which would not
4073ae
correctly move the process into the scope if the application exited
4073ae
right after the task returned.
4073ae
---
4073ae
 gio/gdesktopappinfo.c | 212 +++++++++++++++++++++++++++++-------------
4073ae
 1 file changed, 146 insertions(+), 66 deletions(-)
4073ae
4073ae
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
4073ae
index afdcd42ac..8d0f1688e 100644
4073ae
--- a/gio/gdesktopappinfo.c
4073ae
+++ b/gio/gdesktopappinfo.c
4073ae
@@ -2849,11 +2849,17 @@ create_systemd_scope (GDBusConnection    *session_bus,
4073ae
   g_free (unit_name);
4073ae
 }
4073ae
 
4073ae
+typedef struct {
4073ae
+  GTask *task;
4073ae
+  int fd;
4073ae
+} ScopeCreatedData;
4073ae
+
4073ae
 static void
4073ae
 systemd_scope_created_cb (GObject      *object,
4073ae
                           GAsyncResult *result,
4073ae
                           gpointer      user_data)
4073ae
 {
4073ae
+  ScopeCreatedData *data = user_data;
4073ae
   GVariant *res = NULL;
4073ae
   GError *error = NULL;
4073ae
 
4073ae
@@ -2865,13 +2871,47 @@ systemd_scope_created_cb (GObject      *object,
4073ae
     }
4073ae
 
4073ae
   /* Unblock the waiting wrapper binary. */
4073ae
-  close (GPOINTER_TO_INT (user_data));
4073ae
+
4073ae
+  close (data->fd);
4073ae
+
4073ae
+  if (data->task)
4073ae
+    {
4073ae
+      gint pending;
4073ae
+      pending = GPOINTER_TO_INT (g_task_get_task_data (data->task));
4073ae
+      pending -= 1;
4073ae
+      g_task_set_task_data (data->task, GINT_TO_POINTER (pending), NULL);
4073ae
+
4073ae
+      if (pending == 0 && !g_task_get_completed (data->task))
4073ae
+        g_task_return_boolean (data->task, TRUE);
4073ae
+    }
4073ae
 
4073ae
   if (res)
4073ae
     g_variant_unref (res);
4073ae
+  g_clear_object (&data->task);
4073ae
+  g_free (data);
4073ae
 }
4073ae
 #endif
4073ae
 
4073ae
+static void
4073ae
+launch_uris_with_spawn_flush_cb (GObject      *object,
4073ae
+                                 GAsyncResult *result,
4073ae
+                                 gpointer      user_data)
4073ae
+{
4073ae
+  GTask *task = G_TASK (user_data);
4073ae
+  gint pending;
4073ae
+
4073ae
+  g_dbus_connection_flush_finish (G_DBUS_CONNECTION (object), result, NULL);
4073ae
+
4073ae
+  pending = GPOINTER_TO_INT (g_task_get_task_data (task));
4073ae
+  pending -= 1;
4073ae
+  g_task_set_task_data (task, GINT_TO_POINTER (pending), NULL);
4073ae
+
4073ae
+  if (pending == 0 && !g_task_get_completed (task))
4073ae
+    g_task_return_boolean (task, TRUE);
4073ae
+
4073ae
+  g_object_unref (task);
4073ae
+}
4073ae
+
4073ae
 static gboolean
4073ae
 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                                            GDBusConnection            *session_bus,
4073ae
@@ -2886,9 +2926,10 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                                            gint                        stdin_fd,
4073ae
                                            gint                        stdout_fd,
4073ae
                                            gint                        stderr_fd,
4073ae
-                                           GError                    **error)
4073ae
+                                           GTask                      *task,
4073ae
+                                           GError                    **error_out)
4073ae
 {
4073ae
-  gboolean completed = FALSE;
4073ae
+  GError *error = NULL;
4073ae
   GList *old_uris;
4073ae
   GList *dup_uris;
4073ae
 
4073ae
@@ -2898,8 +2939,15 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
   char *sn_id = NULL;
4073ae
   int argc;
4073ae
 
4073ae
+  /* We may get a task to report back on or an error. But never both. */
4073ae
+  g_assert (!(task && error_out));
4073ae
   g_return_val_if_fail (info != NULL, FALSE);
4073ae
 
4073ae
+  /* Surrounding code must not have set any data on the task
4073ae
+   * (it is cleared before calling this function). */
4073ae
+  if (session_bus && task)
4073ae
+    g_assert (g_task_get_task_data (task) == NULL);
4073ae
+
4073ae
   if (launch_context)
4073ae
     envp = g_app_launch_context_get_environment (launch_context);
4073ae
   else
4073ae
@@ -2922,8 +2970,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
       gpointer             setup_data = user_setup_data;
4073ae
 
4073ae
       old_uris = dup_uris;
4073ae
-      if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
4073ae
-        return FALSE;
4073ae
+      if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, &error))
4073ae
+        goto out;
4073ae
 
4073ae
       /* Get the subset of URIs we're launching with this process */
4073ae
       for (iter = old_uris; iter != NULL && iter != dup_uris; iter = iter->next)
4073ae
@@ -2932,9 +2980,9 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
 
4073ae
       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
4073ae
         {
4073ae
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
-                               _("Unable to find terminal required for application"));
4073ae
-          return FALSE;
4073ae
+          error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
+                                       _("Unable to find terminal required for application"));
4073ae
+          goto out;
4073ae
         }
4073ae
 
4073ae
       if (info->filename)
4073ae
@@ -2991,9 +3039,9 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
        * otherwise our wrapper script will close both sides. */
4073ae
       if (!g_unix_open_pipe (wrapper_data.pipe, 0, NULL))
4073ae
         {
4073ae
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
-                               _("Unable to create pipe for systemd synchronization"));
4073ae
-          return FALSE;
4073ae
+          error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
4073ae
+                                       _("Unable to create pipe for systemd synchronization"));
4073ae
+          goto out;
4073ae
         }
4073ae
 
4073ae
       /* Set CLOEXEC on the write pipe, so we don't need to deal with it in the child. */
4073ae
@@ -3030,7 +3078,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
                                    stdin_fd,
4073ae
                                    stdout_fd,
4073ae
                                    stderr_fd,
4073ae
-                                   error))
4073ae
+                                   &error))
4073ae
         {
4073ae
 #if defined(__linux__) && !defined(__BIONIC__)
4073ae
           close (wrapper_data.pipe[0]);
4073ae
@@ -3049,11 +3097,29 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
       close (wrapper_data.pipe[0]);
4073ae
 
4073ae
       if (session_bus)
4073ae
-        create_systemd_scope (session_bus,
4073ae
-                              info,
4073ae
-                              pid,
4073ae
-                              systemd_scope_created_cb,
4073ae
-                              GINT_TO_POINTER (wrapper_data.pipe[1]));
4073ae
+        {
4073ae
+          ScopeCreatedData *data;
4073ae
+
4073ae
+          data = g_new0 (ScopeCreatedData, 1);
4073ae
+
4073ae
+          if (task)
4073ae
+            {
4073ae
+              gint pending;
4073ae
+              pending = GPOINTER_TO_INT (g_task_get_task_data (task));
4073ae
+              pending += 1;
4073ae
+              g_task_set_task_data (task, GINT_TO_POINTER (pending), NULL);
4073ae
+
4073ae
+              data->task = g_object_ref (task);
4073ae
+            }
4073ae
+
4073ae
+          data->fd = wrapper_data.pipe[1];
4073ae
+
4073ae
+          create_systemd_scope (session_bus,
4073ae
+                                info,
4073ae
+                                pid,
4073ae
+                                systemd_scope_created_cb,
4073ae
+                                data);
4073ae
+        }
4073ae
       else
4073ae
         close (wrapper_data.pipe[1]);
4073ae
 #endif
4073ae
@@ -3088,8 +3154,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
4073ae
     }
4073ae
   while (dup_uris != NULL);
4073ae
 
4073ae
-  completed = TRUE;
4073ae
-
4073ae
 out:
4073ae
   g_strfreev (argv);
4073ae
   g_strfreev (envp);
4073ae
@@ -3097,7 +3161,52 @@ out:
4073ae
   g_list_free (launched_uris);
4073ae
   g_free (sn_id);
4073ae
 
4073ae
-  return completed;
4073ae
+  if (!error)
4073ae
+    {
4073ae
+      if (session_bus && task)
4073ae
+        {
4073ae
+          GCancellable *cancellable = g_task_get_cancellable (task);
4073ae
+          gint pending;
4073ae
+          pending = GPOINTER_TO_INT (g_task_get_task_data (task));
4073ae
+          pending += 1;
4073ae
+          g_task_set_task_data (task, GINT_TO_POINTER (pending), NULL);
4073ae
+
4073ae
+          /* FIXME: The D-Bus message from the notify_desktop_launch() function
4073ae
+           * can be still lost even if flush is called later. See:
4073ae
+           * https://gitlab.freedesktop.org/dbus/dbus/issues/72
4073ae
+           */
4073ae
+          g_dbus_connection_flush (session_bus,
4073ae
+                                   cancellable,
4073ae
+                                   launch_uris_with_spawn_flush_cb,
4073ae
+                                   g_steal_pointer (&task));
4073ae
+        }
4073ae
+      else if (session_bus)
4073ae
+        {
4073ae
+          /* No task available. */
4073ae
+          g_dbus_connection_flush (session_bus,
4073ae
+                                   NULL,
4073ae
+                                   NULL,
4073ae
+                                   NULL);
4073ae
+        }
4073ae
+      else if (task)
4073ae
+        {
4073ae
+          /* Return the given task. */
4073ae
+          g_task_return_boolean (task, TRUE);
4073ae
+          g_object_unref (task);
4073ae
+        }
4073ae
+    }
4073ae
+  else
4073ae
+    {
4073ae
+      if (task)
4073ae
+        {
4073ae
+          g_task_return_error (task, error);
4073ae
+          g_object_unref (task);
4073ae
+        }
4073ae
+      else
4073ae
+        g_propagate_error (error_out, error);
4073ae
+    }
4073ae
+
4073ae
+  return !error;
4073ae
 }
4073ae
 
4073ae
 static gchar *
4073ae
@@ -3246,17 +3355,9 @@ g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
4073ae
     success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
4073ae
                                                          spawn_flags, user_setup, user_setup_data,
4073ae
                                                          pid_callback, pid_callback_data,
4073ae
-                                                         stdin_fd, stdout_fd, stderr_fd, error);
4073ae
+                                                         stdin_fd, stdout_fd, stderr_fd, NULL, error);
4073ae
 
4073ae
-  if (session_bus != NULL)
4073ae
-    {
4073ae
-      /* This asynchronous flush holds a reference until it completes,
4073ae
-       * which ensures that the following unref won't immediately kill
4073ae
-       * the connection if we were the initial owner.
4073ae
-       */
4073ae
-      g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
4073ae
-      g_object_unref (session_bus);
4073ae
-    }
4073ae
+  g_clear_object (&session_bus);
4073ae
 
4073ae
   return success;
4073ae
 }
4073ae
@@ -3310,18 +3411,6 @@ launch_uris_with_dbus_cb (GObject      *object,
4073ae
   g_object_unref (task);
4073ae
 }
4073ae
 
4073ae
-static void
4073ae
-launch_uris_flush_cb (GObject      *object,
4073ae
-                      GAsyncResult *result,
4073ae
-                      gpointer      user_data)
4073ae
-{
4073ae
-  GTask *task = G_TASK (user_data);
4073ae
-
4073ae
-  g_dbus_connection_flush_finish (G_DBUS_CONNECTION (object), result, NULL);
4073ae
-  g_task_return_boolean (task, TRUE);
4073ae
-  g_object_unref (task);
4073ae
-}
4073ae
-
4073ae
 static void
4073ae
 launch_uris_bus_get_cb (GObject      *object,
4073ae
                         GAsyncResult *result,
4073ae
@@ -3330,12 +3419,20 @@ launch_uris_bus_get_cb (GObject      *object,
4073ae
   GTask *task = G_TASK (user_data);
4073ae
   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (g_task_get_source_object (task));
4073ae
   LaunchUrisData *data = g_task_get_task_data (task);
4073ae
+  LaunchUrisData *data_copy = NULL;
4073ae
   GCancellable *cancellable = g_task_get_cancellable (task);
4073ae
   GDBusConnection *session_bus;
4073ae
-  GError *error = NULL;
4073ae
 
4073ae
   session_bus = g_bus_get_finish (result, NULL);
4073ae
 
4073ae
+  data_copy = g_new0 (LaunchUrisData, 1);
4073ae
+  data_copy->appinfo = g_steal_pointer (&data->appinfo);
4073ae
+  data_copy->uris = g_steal_pointer (&data->uris);
4073ae
+  data_copy->context = g_steal_pointer (&data->context);
4073ae
+
4073ae
+  /* Allow other data to be attached to the task. */
4073ae
+  g_task_set_task_data (task, NULL, NULL);
4073ae
+
4073ae
   if (session_bus && info->app_id)
4073ae
     {
4073ae
       /* FIXME: The g_document_portal_add_documents() function, which is called
4073ae
@@ -3343,34 +3440,21 @@ launch_uris_bus_get_cb (GObject      *object,
4073ae
        * uses blocking calls.
4073ae
        */
4073ae
       g_desktop_app_info_launch_uris_with_dbus (info, session_bus,
4073ae
-                                                data->uris, data->context,
4073ae
+                                                data_copy->uris, data_copy->context,
4073ae
                                                 cancellable,
4073ae
                                                 launch_uris_with_dbus_cb,
4073ae
                                                 g_steal_pointer (&task));
4073ae
     }
4073ae
   else
4073ae
     {
4073ae
-      /* FIXME: The D-Bus message from the notify_desktop_launch() function
4073ae
-       * can be still lost even if flush is called later. See:
4073ae
-       * https://gitlab.freedesktop.org/dbus/dbus/issues/72
4073ae
-       */
4073ae
       g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec,
4073ae
-                                                 data->uris, data->context,
4073ae
+                                                 data_copy->uris, data_copy->context,
4073ae
                                                  _SPAWN_FLAGS_DEFAULT, NULL,
4073ae
                                                  NULL, NULL, NULL, -1, -1, -1,
4073ae
-                                                 &error);
4073ae
-      if (error != NULL)
4073ae
-        {
4073ae
-          g_task_return_error (task, g_steal_pointer (&error));
4073ae
-          g_object_unref (task);
4073ae
-        }
4073ae
-      else
4073ae
-        g_dbus_connection_flush (session_bus,
4073ae
-                                 cancellable,
4073ae
-                                 launch_uris_flush_cb,
4073ae
-                                 g_steal_pointer (&task));
4073ae
+                                                 g_steal_pointer (&task), NULL);
4073ae
     }
4073ae
 
4073ae
+  launch_uris_data_free (data_copy);
4073ae
   g_clear_object (&session_bus);
4073ae
 }
4073ae
 
4073ae
@@ -5186,16 +5270,12 @@ g_desktop_app_info_launch_action (GDesktopAppInfo   *info,
4073ae
       if (exec_line)
4073ae
         g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
4073ae
                                                    _SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL,
4073ae
-                                                   -1, -1, -1, NULL);
4073ae
+                                                   -1, -1, -1, NULL, NULL);
4073ae
 
4073ae
       g_free (exec_line);
4073ae
     }
4073ae
 
4073ae
-  if (session_bus != NULL)
4073ae
-    {
4073ae
-      g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
4073ae
-      g_object_unref (session_bus);
4073ae
-    }
4073ae
+  g_clear_object (&session_bus);
4073ae
 }
4073ae
 /* Epilogue {{{1 */
4073ae
 
4073ae
-- 
4073ae
2.31.1
4073ae
4073ae
From 8da8a3ef6df8af6de8bd388192bebe8b51b3e782 Mon Sep 17 00:00:00 2001
4073ae
From: Benjamin Berg <bberg@redhat.com>
4073ae
Date: Thu, 17 Sep 2020 17:35:58 +0200
4073ae
Subject: [PATCH 4/4] gdesktopappinfo: Add SourcePath= to transient systemd
4073ae
 units
4073ae
4073ae
systemd allows setting a SourcePath= which shows the file that the unit
4073ae
has been generated from. KDE is starting to set this and it seems like a
4073ae
good idea, so do the same here.
4073ae
4073ae
See https://invent.kde.org/frameworks/kio/-/merge_requests/124
4073ae
---
4073ae
 gio/gdesktopappinfo.c | 13 +++++++++++++
4073ae
 1 file changed, 13 insertions(+)
4073ae
4073ae
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
4073ae
index 8d0f1688e..a833de4e6 100644
4073ae
--- a/gio/gdesktopappinfo.c
4073ae
+++ b/gio/gdesktopappinfo.c
4073ae
@@ -2777,6 +2777,7 @@ create_systemd_scope (GDBusConnection    *session_bus,
4073ae
 {
4073ae
   GVariantBuilder builder;
4073ae
   const char *app_name = g_get_application_name ();
4073ae
+  const char *source_path = NULL;
4073ae
   char *appid = NULL;
4073ae
   char *appid_escaped = NULL;
4073ae
   char *snid_escaped = NULL;
4073ae
@@ -2802,6 +2803,8 @@ create_systemd_scope (GDBusConnection    *session_bus,
4073ae
    */
4073ae
   unit_name = g_strdup_printf ("app-glib-%s-%d.scope", appid_escaped, pid);
4073ae
 
4073ae
+  source_path = g_desktop_app_info_get_filename (info);
4073ae
+
4073ae
   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ssa(sv)a(sa(sv)))"));
4073ae
   g_variant_builder_add (&builder, "s", unit_name);
4073ae
   g_variant_builder_add (&builder, "s", "fail");
4073ae
@@ -2815,6 +2818,16 @@ create_systemd_scope (GDBusConnection    *session_bus,
4073ae
                            "Description",
4073ae
                            g_variant_new_take_string (g_strdup_printf ("Application launched by %s",
4073ae
                                                                        app_name)));
4073ae
+
4073ae
+  /* If we have a .desktop file, document that the scope has been "generated"
4073ae
+   * from it.
4073ae
+   */
4073ae
+  if (source_path && g_utf8_validate (source_path, -1, NULL))
4073ae
+    g_variant_builder_add (&builder,
4073ae
+                           "(sv)",
4073ae
+                           "SourcePath",
4073ae
+                           g_variant_new_string (source_path));
4073ae
+
4073ae
   g_variant_builder_add (&builder,
4073ae
                          "(sv)",
4073ae
                          "PIDs",
4073ae
-- 
4073ae
2.31.1