You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
855 lines
30 KiB
855 lines
30 KiB
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
|
|
|