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