commit 3c8b3913396c1cdca3fd8e157f623f1f221bd081 Author: CentOS Sources Date: Tue May 17 06:28:09 2022 -0400 import glib2-2.68.4-5.el9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4005667 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/glib-2.68.4.tar.xz diff --git a/.glib2.metadata b/.glib2.metadata new file mode 100644 index 0000000..0e51474 --- /dev/null +++ b/.glib2.metadata @@ -0,0 +1 @@ +bfebd4a5074715962177e8712cec630219f58786 SOURCES/glib-2.68.4.tar.xz diff --git a/SOURCES/1596.patch b/SOURCES/1596.patch new file mode 100644 index 0000000..786ac7c --- /dev/null +++ b/SOURCES/1596.patch @@ -0,0 +1,855 @@ +From 5e42384cc4499293259a8a37a737014a56de34df Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +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 +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 +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 +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 diff --git a/SOURCES/1965.patch b/SOURCES/1965.patch new file mode 100644 index 0000000..59d7cb8 --- /dev/null +++ b/SOURCES/1965.patch @@ -0,0 +1,222 @@ +From 1248b642ad32b0bdf296211c1a0a8817bebf1c66 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 25 Feb 2021 10:35:36 +0000 +Subject: [PATCH 1/2] gversionmacros: Add version macros for GLib 2.70 + +Signed-off-by: Simon McVittie +--- + docs/reference/gio/gio-docs.xml | 4 +++ + docs/reference/glib/glib-docs.xml | 4 +++ + docs/reference/glib/glib-sections.txt | 14 ++++++++ + docs/reference/gobject/gobject-docs.xml | 4 +++ + docs/reference/meson.build | 2 +- + glib/gversionmacros.h | 44 +++++++++++++++++++++++++ + 6 files changed, 71 insertions(+), 1 deletion(-) + +diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml +index 9cd3d0e39..a09d6d31d 100644 +--- a/docs/reference/gio/gio-docs.xml ++++ b/docs/reference/gio/gio-docs.xml +@@ -389,6 +389,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml +index e464fb792..2f5de9e31 100644 +--- a/docs/reference/glib/glib-docs.xml ++++ b/docs/reference/glib/glib-docs.xml +@@ -288,6 +288,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt +index 460a299bf..75994e889 100644 +--- a/docs/reference/glib/glib-sections.txt ++++ b/docs/reference/glib/glib-sections.txt +@@ -138,6 +138,7 @@ GLIB_VERSION_2_62 + GLIB_VERSION_2_64 + GLIB_VERSION_2_66 + GLIB_VERSION_2_68 ++GLIB_VERSION_2_70 + GLIB_VERSION_CUR_STABLE + GLIB_VERSION_PREV_STABLE + GLIB_VERSION_MIN_REQUIRED +@@ -168,6 +169,7 @@ GLIB_AVAILABLE_ENUMERATOR_IN_2_62 + GLIB_AVAILABLE_ENUMERATOR_IN_2_64 + GLIB_AVAILABLE_ENUMERATOR_IN_2_66 + GLIB_AVAILABLE_ENUMERATOR_IN_2_68 ++GLIB_AVAILABLE_ENUMERATOR_IN_2_70 + GLIB_AVAILABLE_IN_ALL + GLIB_AVAILABLE_IN_2_26 + GLIB_AVAILABLE_IN_2_28 +@@ -191,6 +193,7 @@ GLIB_AVAILABLE_IN_2_62 + GLIB_AVAILABLE_IN_2_64 + GLIB_AVAILABLE_IN_2_66 + GLIB_AVAILABLE_IN_2_68 ++GLIB_AVAILABLE_IN_2_70 + GLIB_AVAILABLE_MACRO_IN_2_26 + GLIB_AVAILABLE_MACRO_IN_2_28 + GLIB_AVAILABLE_MACRO_IN_2_30 +@@ -213,12 +216,14 @@ GLIB_AVAILABLE_MACRO_IN_2_62 + GLIB_AVAILABLE_MACRO_IN_2_64 + GLIB_AVAILABLE_MACRO_IN_2_66 + GLIB_AVAILABLE_MACRO_IN_2_68 ++GLIB_AVAILABLE_MACRO_IN_2_70 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_44 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_60 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_62 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_64 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_66 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_68 ++GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 + GLIB_AVAILABLE_TYPE_IN_2_26 + GLIB_AVAILABLE_TYPE_IN_2_28 + GLIB_AVAILABLE_TYPE_IN_2_30 +@@ -241,6 +246,7 @@ GLIB_AVAILABLE_TYPE_IN_2_62 + GLIB_AVAILABLE_TYPE_IN_2_64 + GLIB_AVAILABLE_TYPE_IN_2_66 + GLIB_AVAILABLE_TYPE_IN_2_68 ++GLIB_AVAILABLE_TYPE_IN_2_70 + GLIB_DEPRECATED_ENUMERATOR + GLIB_DEPRECATED_ENUMERATOR_FOR + GLIB_DEPRECATED_ENUMERATOR_IN_2_26 +@@ -287,6 +293,8 @@ GLIB_DEPRECATED_ENUMERATOR_IN_2_66 + GLIB_DEPRECATED_ENUMERATOR_IN_2_66_FOR + GLIB_DEPRECATED_ENUMERATOR_IN_2_68 + GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR ++GLIB_DEPRECATED_ENUMERATOR_IN_2_70 ++GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR + GLIB_DEPRECATED_IN_2_26 + GLIB_DEPRECATED_IN_2_26_FOR + GLIB_DEPRECATED_IN_2_28 +@@ -331,6 +339,8 @@ GLIB_DEPRECATED_IN_2_66 + GLIB_DEPRECATED_IN_2_66_FOR + GLIB_DEPRECATED_IN_2_68 + GLIB_DEPRECATED_IN_2_68_FOR ++GLIB_DEPRECATED_IN_2_70 ++GLIB_DEPRECATED_IN_2_70_FOR + GLIB_DEPRECATED_MACRO + GLIB_DEPRECATED_MACRO_FOR + GLIB_DEPRECATED_MACRO_IN_2_26 +@@ -377,6 +387,8 @@ GLIB_DEPRECATED_MACRO_IN_2_66 + GLIB_DEPRECATED_MACRO_IN_2_66_FOR + GLIB_DEPRECATED_MACRO_IN_2_68 + GLIB_DEPRECATED_MACRO_IN_2_68_FOR ++GLIB_DEPRECATED_MACRO_IN_2_70 ++GLIB_DEPRECATED_MACRO_IN_2_70_FOR + GLIB_DEPRECATED_TYPE + GLIB_DEPRECATED_TYPE_FOR + GLIB_DEPRECATED_TYPE_IN_2_26 +@@ -423,6 +435,8 @@ GLIB_DEPRECATED_TYPE_IN_2_66 + GLIB_DEPRECATED_TYPE_IN_2_66_FOR + GLIB_DEPRECATED_TYPE_IN_2_68 + GLIB_DEPRECATED_TYPE_IN_2_68_FOR ++GLIB_DEPRECATED_TYPE_IN_2_70 ++GLIB_DEPRECATED_TYPE_IN_2_70_FOR + GLIB_VERSION_CUR_STABLE + GLIB_VERSION_PREV_STABLE + +diff --git a/docs/reference/gobject/gobject-docs.xml b/docs/reference/gobject/gobject-docs.xml +index ddbc9f274..e8e7c76d9 100644 +--- a/docs/reference/gobject/gobject-docs.xml ++++ b/docs/reference/gobject/gobject-docs.xml +@@ -208,6 +208,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/meson.build b/docs/reference/meson.build +index 3f09be555..53ca12ff8 100644 +--- a/docs/reference/meson.build ++++ b/docs/reference/meson.build +@@ -7,7 +7,7 @@ + stable_2_series_versions = [ + '26', '28', '30', '32', '34', '36', '38', + '40', '42', '44', '46', '48', '50', '52', '54', '56', '58', +- '60', '62', '64', '66', '68', ++ '60', '62', '64', '66', '68', '70', + ] + + ignore_decorators = [ +diff --git a/glib/gversionmacros.h b/glib/gversionmacros.h +index 77486eafb..d052709cf 100644 +--- a/glib/gversionmacros.h ++++ b/glib/gversionmacros.h +@@ -255,6 +255,16 @@ + */ + #define GLIB_VERSION_2_68 (G_ENCODE_VERSION (2, 68)) + ++/** ++ * GLIB_VERSION_2_70: ++ * ++ * A macro that evaluates to the 2.70 version of GLib, in a format ++ * that can be used by the C pre-processor. ++ * ++ * Since: 2.70 ++ */ ++#define GLIB_VERSION_2_70 (G_ENCODE_VERSION (2, 70)) ++ + /** + * GLIB_VERSION_CUR_STABLE: + * +@@ -1076,4 +1086,38 @@ + # define GLIB_AVAILABLE_TYPE_IN_2_68 + #endif + ++#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_70 ++# define GLIB_DEPRECATED_IN_2_70 GLIB_DEPRECATED ++# define GLIB_DEPRECATED_IN_2_70_FOR(f) GLIB_DEPRECATED_FOR(f) ++# define GLIB_DEPRECATED_MACRO_IN_2_70 GLIB_DEPRECATED_MACRO ++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f) GLIB_DEPRECATED_MACRO_FOR(f) ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70 GLIB_DEPRECATED_ENUMERATOR ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f) GLIB_DEPRECATED_ENUMERATOR_FOR(f) ++# define GLIB_DEPRECATED_TYPE_IN_2_70 GLIB_DEPRECATED_TYPE ++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f) GLIB_DEPRECATED_TYPE_FOR(f) ++#else ++# define GLIB_DEPRECATED_IN_2_70 _GLIB_EXTERN ++# define GLIB_DEPRECATED_IN_2_70_FOR(f) _GLIB_EXTERN ++# define GLIB_DEPRECATED_MACRO_IN_2_70 ++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f) ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70 ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f) ++# define GLIB_DEPRECATED_TYPE_IN_2_70 ++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f) ++#endif ++ ++#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_70 ++# define GLIB_AVAILABLE_IN_2_70 GLIB_UNAVAILABLE(2, 70) ++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 GLIB_UNAVAILABLE_STATIC_INLINE(2, 70) ++# define GLIB_AVAILABLE_MACRO_IN_2_70 GLIB_UNAVAILABLE_MACRO(2, 70) ++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70 GLIB_UNAVAILABLE_ENUMERATOR(2, 70) ++# define GLIB_AVAILABLE_TYPE_IN_2_70 GLIB_UNAVAILABLE_TYPE(2, 70) ++#else ++# define GLIB_AVAILABLE_IN_2_70 _GLIB_EXTERN ++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 ++# define GLIB_AVAILABLE_MACRO_IN_2_70 ++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70 ++# define GLIB_AVAILABLE_TYPE_IN_2_70 ++#endif ++ + #endif /* __G_VERSION_MACROS_H__ */ +-- +GitLab diff --git a/SOURCES/1968.patch b/SOURCES/1968.patch new file mode 100644 index 0000000..1809214 --- /dev/null +++ b/SOURCES/1968.patch @@ -0,0 +1,1052 @@ +From 9e69f8b280afe8eccd9188cc53b8117e1b238db7 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 12 Oct 2021 15:52:18 -0500 +Subject: [PATCH 01/10] gspawn: use close_and_invalidate more + +--- + glib/gspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index a15fb1ca1..5d8422869 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1710,7 +1710,7 @@ do_exec (gint child_err_report_fd, + child_err_report_fd = safe_dup (child_err_report_fd); + + safe_dup2 (source_fds[i], target_fds[i]); +- (void) close (source_fds[i]); ++ close_and_invalidate (&source_fds[i]); + } + } + } +-- +2.33.1 + +From fe2148fd5dd4f2e5c413c5cc0bb56c4a19304887 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 10:43:52 -0500 +Subject: [PATCH 02/10] gspawn: Improve error message when dup fails + +This error message is no longer accurate now that we allow arbitrary fd +remapping. +--- + glib/gspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 5d8422869..e214a3998 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -2363,7 +2363,7 @@ fork_exec (gboolean intermediate_child, + g_set_error (error, + G_SPAWN_ERROR, + G_SPAWN_ERROR_FAILED, +- _("Failed to redirect output or input of child process (%s)"), ++ _("Failed to duplicate file descriptor for child process (%s)"), + g_strerror (buf[1])); + + break; +-- +2.33.1 + +From 566eccdb0a2594b4d3ec13c7443028d968b41af8 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 12 Oct 2021 15:33:59 -0500 +Subject: [PATCH 03/10] gspawn: fix hangs when duping child_err_report_fd + +In case child_err_report_fd conflicts with one of the target_fds, the +code here is careful to dup child_err_report_fd in order to avoid +conflating the two. It was a good idea, but evidently was not tested, +because the newly-created fd is not created with CLOEXEC set. This means +it stays open in the child process, causing the parent to hang forever +waiting to read from the other end of the pipe. Oops! + +The fix is simple: just set CLOEXEC. This removes our only usage of the +safe_dup() function, so it can be dropped. + +Fixes #2506 +--- + glib/gspawn.c | 16 +--------------- + 1 file changed, 1 insertion(+), 15 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index e214a3998..8bbe573f7 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1500,20 +1500,6 @@ safe_closefrom (int lowfd) + #endif + } + +-/* This function is called between fork() and exec() and hence must be +- * async-signal-safe (see signal-safety(7)). */ +-static gint +-safe_dup (gint fd) +-{ +- gint ret; +- +- do +- ret = dup (fd); +- while (ret < 0 && (errno == EINTR || errno == EBUSY)); +- +- return ret; +-} +- + /* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ + static gint +@@ -1707,7 +1693,7 @@ do_exec (gint child_err_report_fd, + else + { + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = safe_dup (child_err_report_fd); ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd); + + safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); +-- +2.33.1 + +From b703fa8b760ac9272c5a0ed3e3763b2f71ecf574 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 10:44:57 -0500 +Subject: [PATCH 04/10] gspawn: fix fd remapping conflation issue + +We currently dup all source fds to avoid possible conflation with the +target fds, but fail to consider that the result of a dup might itself +conflict with one of the target fds. Solve this the easy way by duping +all source_fds to values that are greater than the largest fd in +target_fds. + +Fixes #2503 +--- + glib/gspawn.c | 43 +++++++++++++++++++++++++------------------ + 1 file changed, 25 insertions(+), 18 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 8bbe573f7..2b48b5600 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1258,13 +1258,13 @@ unset_cloexec (int fd) + /* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ + static int +-dupfd_cloexec (int parent_fd) ++dupfd_cloexec (int old_fd, int new_fd_min) + { + int fd, errsv; + #ifdef F_DUPFD_CLOEXEC + do + { +- fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3); ++ fd = fcntl (old_fd, F_DUPFD_CLOEXEC, new_fd_min); + errsv = errno; + } + while (fd == -1 && errsv == EINTR); +@@ -1275,7 +1275,7 @@ dupfd_cloexec (int parent_fd) + int result, flags; + do + { +- fd = fcntl (parent_fd, F_DUPFD, 3); ++ fd = fcntl (old_fd, F_DUPFD, new_fd_min); + errsv = errno; + } + while (fd == -1 && errsv == EINTR); +@@ -1563,6 +1563,7 @@ do_exec (gint child_err_report_fd, + gpointer user_data) + { + gsize i; ++ gint max_target_fd = 0; + + if (working_directory && chdir (working_directory) < 0) + write_err_and_exit (child_err_report_fd, +@@ -1661,39 +1662,45 @@ do_exec (gint child_err_report_fd, + /* + * Work through the @source_fds and @target_fds mapping. + * +- * Based on code derived from ++ * Based on code originally derived from + * gnome-terminal:src/terminal-screen.c:terminal_screen_child_setup(), +- * used under the LGPLv2+ with permission from author. ++ * used under the LGPLv2+ with permission from author. (The code has ++ * since migrated to vte:src/spawn.cc:SpawnContext::exec and is no longer ++ * terribly similar to what we have here.) + */ + +- /* Basic fd assignments (where source == target) we can just unset FD_CLOEXEC +- * +- * If we're doing remapping fd assignments, we need to handle +- * the case where the user has specified e.g.: +- * 5 -> 4, 4 -> 6 +- * +- * We do this by duping the source fds temporarily in a first pass. +- * +- * If any of the @target_fds conflict with @child_err_report_fd, dup the +- * latter so it doesn’t get conflated. +- */ + if (n_fds > 0) + { ++ for (i = 0; i < n_fds; i++) ++ max_target_fd = MAX (max_target_fd, target_fds[i]); ++ ++ /* If we're doing remapping fd assignments, we need to handle ++ * the case where the user has specified e.g. 5 -> 4, 4 -> 6. ++ * We do this by duping all source fds, taking care to ensure the new ++ * fds are larger than any target fd to avoid introducing new conflicts. ++ */ + for (i = 0; i < n_fds; i++) + { + if (source_fds[i] != target_fds[i]) +- source_fds[i] = dupfd_cloexec (source_fds[i]); ++ source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); + } ++ + for (i = 0; i < n_fds; i++) + { ++ /* For basic fd assignments (where source == target), we can just ++ * unset FD_CLOEXEC. ++ */ + if (source_fds[i] == target_fds[i]) + { + unset_cloexec (source_fds[i]); + } + else + { ++ /* If any of the @target_fds conflict with @child_err_report_fd, ++ * dup it so it doesn’t get conflated. ++ */ + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = dupfd_cloexec (child_err_report_fd); ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); + + safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); +-- +2.33.1 + +From ecc3538a942760e8b403c319d359711c8e166778 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 25 Feb 2021 12:20:39 -0600 +Subject: [PATCH 05/10] gspawn: Implement fd remapping for posix_spawn codepath + +This means that GSubprocess will (sometimes) be able to use the +optimized posix_spawn codepath instead of having to fall back to +fork/exec. +--- + glib/gspawn.c | 65 +++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 58 insertions(+), 7 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 2b48b5600..9ef78dbe1 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1786,9 +1786,14 @@ do_posix_spawn (const gchar * const *argv, + gint *child_close_fds, + gint stdin_fd, + gint stdout_fd, +- gint stderr_fd) ++ gint stderr_fd, ++ const gint *source_fds, ++ const gint *target_fds, ++ gsize n_fds) + { + pid_t pid; ++ gint *duped_source_fds = NULL; ++ gint max_target_fd = 0; + const gchar * const *argv_pass; + posix_spawnattr_t attr; + posix_spawn_file_actions_t file_actions; +@@ -1797,7 +1802,8 @@ do_posix_spawn (const gchar * const *argv, + GSList *child_close = NULL; + GSList *elem; + sigset_t mask; +- int i, r; ++ size_t i; ++ int r; + + if (*argv[0] == '\0') + { +@@ -1911,6 +1917,43 @@ do_posix_spawn (const gchar * const *argv, + goto out_close_fds; + } + ++ /* If source_fds[i] != target_fds[i], we need to handle the case ++ * where the user has specified, e.g., 5 -> 4, 4 -> 6. We do this ++ * by duping the source fds, taking care to ensure the new fds are ++ * larger than any target fd to avoid introducing new conflicts. ++ * ++ * If source_fds[i] == target_fds[i], then we just need to leak ++ * the fd into the child process, which we *could* do by temporarily ++ * unsetting CLOEXEC and then setting it again after we spawn if ++ * it was originally set. POSIX requires that the addup2 action unset ++ * CLOEXEC if source and target are identical, so you'd think doing it ++ * manually wouldn't be needed, but unfortunately as of 2021 many ++ * libcs still don't do so. Example nonconforming libcs: ++ * Bionic: https://android.googlesource.com/platform/bionic/+/f6e5b582604715729b09db3e36a7aeb8c24b36a4/libc/bionic/spawn.cpp#71 ++ * uclibc-ng: https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/librt/spawn.c?id=7c36bcae09d66bbaa35cbb02253ae0556f42677e#n88 ++ * ++ * Anyway, unsetting CLOEXEC ourselves would open a small race window ++ * where the fd could be inherited into a child process if another ++ * thread spawns something at the same time, because we have not ++ * called fork() and are multithreaded here. This race is avoidable by ++ * using dupfd_cloexec, which we already have to do to handle the ++ * source_fds[i] != target_fds[i] case. So let's always do it! ++ */ ++ ++ for (i = 0; i < n_fds; i++) ++ max_target_fd = MAX (max_target_fd, target_fds[i]); ++ ++ duped_source_fds = g_new (gint, n_fds); ++ for (i = 0; i < n_fds; i++) ++ duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ ++ for (i = 0; i < n_fds; i++) ++ { ++ r = posix_spawn_file_actions_adddup2 (&file_actions, duped_source_fds[i], target_fds[i]); ++ if (r != 0) ++ goto out_close_fds; ++ } ++ + /* Intentionally close the fds in the child as the last file action, + * having been careful not to add the same fd to this list twice. + * +@@ -1940,9 +1983,16 @@ do_posix_spawn (const gchar * const *argv, + *child_pid = pid; + + out_close_fds: +- for (i = 0; i < num_parent_close_fds; i++) ++ for (i = 0; i < (size_t) num_parent_close_fds; i++) + close_and_invalidate (&parent_close_fds [i]); + ++ if (duped_source_fds != NULL) ++ { ++ for (i = 0; i < n_fds; i++) ++ close_and_invalidate (&duped_source_fds[i]); ++ g_free (duped_source_fds); ++ } ++ + posix_spawn_file_actions_destroy (&file_actions); + out_free_spawnattr: + posix_spawnattr_destroy (&attr); +@@ -2030,10 +2080,8 @@ fork_exec (gboolean intermediate_child, + child_close_fds[n_child_close_fds++] = -1; + + #ifdef POSIX_SPAWN_AVAILABLE +- /* FIXME: Handle @source_fds and @target_fds in do_posix_spawn() using the +- * file actions API. */ + if (!intermediate_child && working_directory == NULL && !close_descriptors && +- !search_path_from_envp && child_setup == NULL && n_fds == 0) ++ !search_path_from_envp && child_setup == NULL) + { + g_trace_mark (G_TRACE_CURRENT_TIME, 0, + "GLib", "posix_spawn", +@@ -2050,7 +2098,10 @@ fork_exec (gboolean intermediate_child, + child_close_fds, + stdin_fd, + stdout_fd, +- stderr_fd); ++ stderr_fd, ++ source_fds, ++ target_fds, ++ n_fds); + if (status == 0) + goto success; + +-- +2.33.1 + +From 731d6c32105dc97f2b777ef9a34c6b76d1489c04 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 25 Feb 2021 12:21:38 -0600 +Subject: [PATCH 06/10] gsubprocess: ensure we test fd remapping on the + posix_spawn() codepath + +We should run test_pass_fd twice, once using gspawn's fork/exec codepath +and once attempting to use its posix_spawn() codepath. There's no +guarantee we'll actually get the posix_spawn() codepath, but it works +for now on Linux. + +For good measure, run it a third time with no flags at all. + +This causes the test to fail if I separately break the fd remapping +implementation. Without this, we fail to test fd remapping on the +posix_spawn() codepath. +--- + gio/tests/gsubprocess.c | 44 ++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 41 insertions(+), 3 deletions(-) + +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index 7e22678ec..ba49c1c43 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -1697,7 +1697,8 @@ test_child_setup (void) + } + + static void +-test_pass_fd (void) ++do_test_pass_fd (GSubprocessFlags flags, ++ GSpawnChildSetupFunc child_setup) + { + GError *local_error = NULL; + GError **error = &local_error; +@@ -1722,9 +1723,11 @@ test_pass_fd (void) + needdup_fd_str = g_strdup_printf ("%d", needdup_pipefds[1] + 1); + + args = get_test_subprocess_args ("write-to-fds", basic_fd_str, needdup_fd_str, NULL); +- launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); ++ launcher = g_subprocess_launcher_new (flags); + g_subprocess_launcher_take_fd (launcher, basic_pipefds[1], basic_pipefds[1]); + g_subprocess_launcher_take_fd (launcher, needdup_pipefds[1], needdup_pipefds[1] + 1); ++ if (child_setup != NULL) ++ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); + proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); + g_ptr_array_free (args, TRUE); + g_assert_no_error (local_error); +@@ -1754,6 +1757,39 @@ test_pass_fd (void) + g_object_unref (proc); + } + ++static void ++test_pass_fd (void) ++{ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, NULL); ++} ++ ++static void ++empty_child_setup (gpointer user_data) ++{ ++} ++ ++static void ++test_pass_fd_empty_child_setup (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++} ++ ++static void ++test_pass_fd_inherit_fds (void) ++{ ++ /* Try to test the optimized posix_spawn codepath instead of ++ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's ++ * posix_spawn codepath does not currently handle closing ++ * non-inherited fds. Note that using INHERIT_FDS means our testing of ++ * g_subprocess_launcher_take_fd() is less-comprehensive than when ++ * using G_SUBPROCESS_FLAGS_NONE. ++ */ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++} ++ + #endif + + static void +@@ -1891,7 +1927,9 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file); + g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd); + g_test_add_func ("/gsubprocess/child-setup", test_child_setup); +- g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd); ++ g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd); ++ g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup); ++ g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + +From 4608940466a04a32d4e6e71dbe872cfecb136118 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 11:01:33 -0500 +Subject: [PATCH 07/10] gspawn: Check from errors from safe_dup2() and + dupfd_cloexec() + +Although unlikely, these functions can fail, e.g. if we run out of file +descriptors. Check for errors to improve robustness. This is especially +important now that I changed our use of dupfd_cloexec() to avoid +returning fds smaller than the largest fd in target_fds. An application +that attempts to remap to the highest-allowed fd value deserves at least +some sort of attempt at error reporting, not silent failure. +--- + glib/gspawn.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 9ef78dbe1..7ef678047 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1572,7 +1572,6 @@ do_exec (gint child_err_report_fd, + /* Redirect pipes as required */ + if (stdin_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stdin_fd, 0) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1588,13 +1587,14 @@ do_exec (gint child_err_report_fd, + if (read_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (read_null, 0); ++ if (safe_dup2 (read_null, 0) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&read_null); + } + + if (stdout_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stdout_fd, 1) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1609,13 +1609,14 @@ do_exec (gint child_err_report_fd, + if (write_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (write_null, 1); ++ if (safe_dup2 (write_null, 1) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&write_null); + } + + if (stderr_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stderr_fd, 2) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1630,7 +1631,9 @@ do_exec (gint child_err_report_fd, + if (write_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (write_null, 2); ++ if (safe_dup2 (write_null, 2) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&write_null); + } + +@@ -1643,7 +1646,8 @@ do_exec (gint child_err_report_fd, + { + if (child_setup == NULL && n_fds == 0) + { +- safe_dup2 (child_err_report_fd, 3); ++ if (safe_dup2 (child_err_report_fd, 3) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + set_cloexec (GINT_TO_POINTER (0), 3); + safe_closefrom (4); + child_err_report_fd = 3; +@@ -1682,7 +1686,11 @@ do_exec (gint child_err_report_fd, + for (i = 0; i < n_fds; i++) + { + if (source_fds[i] != target_fds[i]) +- source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ { ++ source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ if (source_fds[i] < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); ++ } + } + + for (i = 0; i < n_fds; i++) +@@ -1700,9 +1708,15 @@ do_exec (gint child_err_report_fd, + * dup it so it doesn’t get conflated. + */ + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); ++ { ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); ++ if (child_err_report_fd < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); ++ } ++ ++ if (safe_dup2 (source_fds[i], target_fds[i]) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + +- safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); + } + } +@@ -1945,7 +1959,11 @@ do_posix_spawn (const gchar * const *argv, + + duped_source_fds = g_new (gint, n_fds); + for (i = 0; i < n_fds; i++) +- duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ { ++ duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ if (duped_source_fds[i] < 0) ++ goto out_close_fds; ++ } + + for (i = 0; i < n_fds; i++) + { +-- +2.33.1 + +From 0198b6a1c8c215f524d7c6ed2d240fb1b31d9865 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 20 Oct 2021 16:51:44 -0500 +Subject: [PATCH 08/10] gspawn: add new error message for open() failures + +Reporting these as dup2() failures is bogus. +--- + glib/gspawn.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 7ef678047..c2fe306dc 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1532,6 +1532,7 @@ enum + { + CHILD_CHDIR_FAILED, + CHILD_EXEC_FAILED, ++ CHILD_OPEN_FAILED, + CHILD_DUP2_FAILED, + CHILD_FORK_FAILED + }; +@@ -1586,7 +1587,7 @@ do_exec (gint child_err_report_fd, + gint read_null = safe_open ("/dev/null", O_RDONLY); + if (read_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (read_null, 0) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1608,7 +1609,7 @@ do_exec (gint child_err_report_fd, + gint write_null = safe_open ("/dev/null", O_WRONLY); + if (write_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (write_null, 1) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1630,7 +1631,7 @@ do_exec (gint child_err_report_fd, + gint write_null = safe_open ("/dev/null", O_WRONLY); + if (write_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (write_null, 2) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -2420,7 +2421,15 @@ fork_exec (gboolean intermediate_child, + g_strerror (buf[1])); + + break; +- ++ ++ case CHILD_OPEN_FAILED: ++ g_set_error (error, ++ G_SPAWN_ERROR, ++ G_SPAWN_ERROR_FAILED, ++ _("Failed to open file to remap file descriptor (%s)"), ++ g_strerror (buf[1])); ++ break; ++ + case CHILD_DUP2_FAILED: + g_set_error (error, + G_SPAWN_ERROR, +-- +2.33.1 + +From e4abb5f3db85b2f730e192e6398f26934e41ba21 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 26 Oct 2021 21:27:15 -0500 +Subject: [PATCH 09/10] Add tests for GSubprocess fd conflation issues + +This tests for #2503. It's fragile, but there is no non-fragile way to +test this. If the test breaks in the future, it will pass without +successfully testing the bug, not fail spuriously, so I think this is +OK. +--- + gio/tests/gsubprocess-testprog.c | 53 +++++++++++- + gio/tests/gsubprocess.c | 144 +++++++++++++++++++++++++++++++ + 2 files changed, 195 insertions(+), 2 deletions(-) + +diff --git a/gio/tests/gsubprocess-testprog.c b/gio/tests/gsubprocess-testprog.c +index c9b06c2a2..58cb1c71d 100644 +--- a/gio/tests/gsubprocess-testprog.c ++++ b/gio/tests/gsubprocess-testprog.c +@@ -5,8 +5,6 @@ + #include + #ifdef G_OS_UNIX + #include +-#include +-#include + #else + #include + #endif +@@ -150,6 +148,55 @@ write_to_fds (int argc, char **argv) + return 0; + } + ++static int ++read_from_fd (int argc, char **argv) ++{ ++ int fd; ++ const char expectedResult[] = "Yay success!"; ++ guint8 buf[sizeof (expectedResult) + 1]; ++ gsize bytes_read; ++ FILE *f; ++ ++ if (argc != 3) ++ { ++ g_print ("Usage: %s read-from-fd FD\n", argv[0]); ++ return 1; ++ } ++ ++ fd = atoi (argv[2]); ++ if (fd == 0) ++ { ++ g_warning ("Argument \"%s\" does not look like a valid nonzero file descriptor", argv[2]); ++ return 1; ++ } ++ ++ f = fdopen (fd, "r"); ++ if (f == NULL) ++ { ++ g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno)); ++ return 1; ++ } ++ ++ bytes_read = fread (buf, 1, sizeof (buf), f); ++ if (bytes_read != sizeof (expectedResult)) ++ { ++ g_warning ("Read %zu bytes, but expected %zu", bytes_read, sizeof (expectedResult)); ++ return 1; ++ } ++ ++ if (memcmp (expectedResult, buf, sizeof (expectedResult)) != 0) ++ { ++ buf[sizeof (expectedResult)] = '\0'; ++ g_warning ("Expected \"%s\" but read \"%s\"", expectedResult, (char *)buf); ++ return 1; ++ } ++ ++ if (fclose (f) == -1) ++ g_assert_not_reached (); ++ ++ return 0; ++} ++ + static int + env_mode (int argc, char **argv) + { +@@ -242,6 +289,8 @@ main (int argc, char **argv) + return sleep_forever_mode (argc, argv); + else if (strcmp (mode, "write-to-fds") == 0) + return write_to_fds (argc, argv); ++ else if (strcmp (mode, "read-from-fd") == 0) ++ return read_from_fd (argc, argv); + else if (strcmp (mode, "env") == 0) + return env_mode (argc, argv); + else if (strcmp (mode, "cwd") == 0) +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index ba49c1c43..a6e24c2e8 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1790,6 +1791,146 @@ test_pass_fd_inherit_fds (void) + do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); + } + ++static void ++do_test_fd_conflation (GSubprocessFlags flags, ++ GSpawnChildSetupFunc child_setup) ++{ ++ char success_message[] = "Yay success!"; ++ GError *error = NULL; ++ GOutputStream *output_stream; ++ GSubprocessLauncher *launcher; ++ GSubprocess *proc; ++ GPtrArray *args; ++ int unused_pipefds[2]; ++ int pipefds[2]; ++ gsize bytes_written; ++ gboolean success; ++ char *fd_str; ++ ++ /* This test must run in a new process because it is extremely sensitive to ++ * order of opened fds. ++ */ ++ if (!g_test_subprocess ()) ++ { ++ g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); ++ g_test_trap_assert_passed (); ++ return; ++ } ++ ++ g_unix_open_pipe (unused_pipefds, FD_CLOEXEC, &error); ++ g_assert_no_error (error); ++ ++ g_unix_open_pipe (pipefds, FD_CLOEXEC, &error); ++ g_assert_no_error (error); ++ ++ /* The fds should be sequential since we are in a new process. */ ++ g_assert_cmpint (unused_pipefds[0] /* 3 */, ==, unused_pipefds[1] - 1); ++ g_assert_cmpint (unused_pipefds[1] /* 4 */, ==, pipefds[0] - 1); ++ g_assert_cmpint (pipefds[0] /* 5 */, ==, pipefds[1] /* 6 */ - 1); ++ ++ /* Because GSubprocess allows arbitrary remapping of fds, it has to be careful ++ * to avoid fd conflation issues, e.g. it should properly handle 5 -> 4 and ++ * 4 -> 5 at the same time. GIO previously attempted to handle this by naively ++ * dup'ing the source fds, but this was not good enough because it was ++ * possible that the dup'ed result could still conflict with one of the target ++ * fds. For example: ++ * ++ * source_fd 5 -> target_fd 9, source_fd 3 -> target_fd 7 ++ * ++ * dup(5) -> dup returns 8 ++ * dup(3) -> dup returns 9 ++ * ++ * After dup'ing, we wind up with: 8 -> 9, 9 -> 7. That means that after we ++ * dup2(8, 9), we have clobbered fd 9 before we dup2(9, 7). The end result is ++ * we have remapped 5 -> 9 as expected, but then remapped 5 -> 7 instead of ++ * 3 -> 7 as the application intended. ++ * ++ * This issue has been fixed in the simplest way possible, by passing a ++ * minimum fd value when using F_DUPFD_CLOEXEC that is higher than any of the ++ * target fds, to guarantee all source fds are different than all target fds, ++ * eliminating any possibility of conflation. ++ * ++ * Anyway, that is why we have the unused_pipefds here. We need to open fds in ++ * a certain order in order to trick older GSubprocess into conflating the ++ * fds. The primary goal of this test is to ensure this particular conflation ++ * issue is not reintroduced. See glib#2503. ++ * ++ * Be aware this test is necessarily extremely fragile. To reproduce these ++ * bugs, it relies on internals of gspawn and gmain that will likely change ++ * in the future, eventually causing this test to no longer test the the bugs ++ * it was originally designed to test. That is OK! If the test fails, at ++ * least you know *something* is wrong. ++ */ ++ launcher = g_subprocess_launcher_new (flags); ++ g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */); ++ g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */); ++ if (child_setup != NULL) ++ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); ++ fd_str = g_strdup_printf ("%d", pipefds[1] + 3); ++ args = get_test_subprocess_args ("read-from-fd", fd_str, NULL); ++ proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (proc); ++ g_ptr_array_free (args, TRUE); ++ g_object_unref (launcher); ++ g_free (fd_str); ++ ++ /* Close the read ends of the pipes. */ ++ close (unused_pipefds[0]); ++ close (pipefds[0]); ++ ++ /* Also close the write end of the unused pipe. */ ++ close (unused_pipefds[1]); ++ ++ /* So now pipefds[0] should be inherited into the subprocess as ++ * pipefds[1] + 2, and unused_pipefds[0] should be inherited as ++ * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify ++ * that it reads the expected data. But older broken GIO will accidentally ++ * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess ++ * to hang trying to read from the wrong pipe. ++ */ ++ output_stream = g_unix_output_stream_new (pipefds[1], TRUE); ++ success = g_output_stream_write_all (output_stream, ++ success_message, sizeof (success_message), ++ &bytes_written, ++ NULL, ++ &error); ++ g_assert_no_error (error); ++ g_assert_cmpint (bytes_written, ==, sizeof (success_message)); ++ g_assert_true (success); ++ g_object_unref (output_stream); ++ ++ success = g_subprocess_wait_check (proc, NULL, &error); ++ g_assert_no_error (error); ++ g_object_unref (proc); ++} ++ ++static void ++test_fd_conflation (void) ++{ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL); ++} ++ ++static void ++test_fd_conflation_empty_child_setup (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++} ++ ++static void ++test_fd_conflation_inherit_fds (void) ++{ ++ /* Try to test the optimized posix_spawn codepath instead of ++ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's ++ * posix_spawn codepath does not currently handle closing ++ * non-inherited fds. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++} ++ + #endif + + static void +@@ -1930,6 +2071,9 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd); + g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup); + g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds); ++ g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation); ++ g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup); ++ g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + +From 5542612c805857a244561ec160e904dd302ae799 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 27 Oct 2021 18:30:47 -0500 +Subject: [PATCH 10/10] Add test for child_err_report_fd conflation with target + fds + +This tests for glib#2506. +--- + gio/tests/gsubprocess.c | 42 ++++++++++++++++++++++++++++++++++------- + 1 file changed, 35 insertions(+), 7 deletions(-) + +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index a6e24c2e8..4629cdea7 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -1793,7 +1793,8 @@ test_pass_fd_inherit_fds (void) + + static void + do_test_fd_conflation (GSubprocessFlags flags, +- GSpawnChildSetupFunc child_setup) ++ GSpawnChildSetupFunc child_setup, ++ gboolean test_child_err_report_fd) + { + char success_message[] = "Yay success!"; + GError *error = NULL; +@@ -1803,6 +1804,7 @@ do_test_fd_conflation (GSubprocessFlags flags, + GPtrArray *args; + int unused_pipefds[2]; + int pipefds[2]; ++ int fd_to_pass_to_child; + gsize bytes_written; + gboolean success; + char *fd_str; +@@ -1855,18 +1857,26 @@ do_test_fd_conflation (GSubprocessFlags flags, + * fds. The primary goal of this test is to ensure this particular conflation + * issue is not reintroduced. See glib#2503. + * ++ * This test also has an alternate mode of operation where it instead tests ++ * for conflation with gspawn's child_err_report_fd, glib#2506. ++ * + * Be aware this test is necessarily extremely fragile. To reproduce these + * bugs, it relies on internals of gspawn and gmain that will likely change + * in the future, eventually causing this test to no longer test the the bugs + * it was originally designed to test. That is OK! If the test fails, at + * least you know *something* is wrong. + */ ++ if (test_child_err_report_fd) ++ fd_to_pass_to_child = pipefds[1] + 2 /* 8 */; ++ else ++ fd_to_pass_to_child = pipefds[1] + 3 /* 9 */; ++ + launcher = g_subprocess_launcher_new (flags); +- g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */); ++ g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, fd_to_pass_to_child); + g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */); + if (child_setup != NULL) + g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); +- fd_str = g_strdup_printf ("%d", pipefds[1] + 3); ++ fd_str = g_strdup_printf ("%d", fd_to_pass_to_child); + args = get_test_subprocess_args ("read-from-fd", fd_str, NULL); + proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error); + g_assert_no_error (error); +@@ -1882,12 +1892,20 @@ do_test_fd_conflation (GSubprocessFlags flags, + /* Also close the write end of the unused pipe. */ + close (unused_pipefds[1]); + +- /* So now pipefds[0] should be inherited into the subprocess as ++ /* If doing our normal test: ++ * ++ * So now pipefds[0] should be inherited into the subprocess as + * pipefds[1] + 2, and unused_pipefds[0] should be inherited as + * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify + * that it reads the expected data. But older broken GIO will accidentally + * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess + * to hang trying to read from the wrong pipe. ++ * ++ * If testing conflation with child_err_report_fd: ++ * ++ * We are actually already done. The real test succeeded if we made it this ++ * far without hanging while spawning the child. But let's continue with our ++ * write and read anyway, to ensure things are good. + */ + output_stream = g_unix_output_stream_new (pipefds[1], TRUE); + success = g_output_stream_write_all (output_stream, +@@ -1908,7 +1926,7 @@ do_test_fd_conflation (GSubprocessFlags flags, + static void + test_fd_conflation (void) + { +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL, FALSE); + } + + static void +@@ -1917,7 +1935,7 @@ test_fd_conflation_empty_child_setup (void) + /* Using a child setup function forces gspawn to use fork/exec + * rather than posix_spawn. + */ +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, FALSE); + } + + static void +@@ -1928,7 +1946,16 @@ test_fd_conflation_inherit_fds (void) + * posix_spawn codepath does not currently handle closing + * non-inherited fds. + */ +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL, FALSE); ++} ++ ++static void ++test_fd_conflation_child_err_report_fd (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, TRUE); + } + + #endif +@@ -2074,6 +2101,7 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation); + g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup); + g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds); ++ g_test_add_func ("/gsubprocess/fd-conflation/child-err-report-fd", test_fd_conflation_child_err_report_fd); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + diff --git a/SOURCES/2194.patch b/SOURCES/2194.patch new file mode 100644 index 0000000..5c9aaf2 --- /dev/null +++ b/SOURCES/2194.patch @@ -0,0 +1,920 @@ +From 2e500304e304e45042a59855319ff0379b1978b3 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Tue, 27 Jul 2021 17:24:17 +0200 +Subject: [PATCH 1/4] tests: Remove unused constant in GMemoryMonitor test + +--- + gio/tests/memory-monitor-dbus.py.in | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in +index cd16cf4e3..7823e7309 100755 +--- a/gio/tests/memory-monitor-dbus.py.in ++++ b/gio/tests/memory-monitor-dbus.py.in +@@ -31,9 +31,6 @@ try: + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +- # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") +- XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" +- + class TestLowMemoryMonitor(dbusmock.DBusTestCase): + '''Test GMemoryMonitorDBus''' + +-- +GitLab + + +From a7000cd989438b01e599b2cfa8b6d5a360bfd102 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 28 Jul 2021 15:10:16 +0200 +Subject: [PATCH 2/4] gio: g_clear_signal_handler() can handle NULL args + +--- + gio/gmemorymonitordbus.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c +index a34a58d3b..08dc53df1 100644 +--- a/gio/gmemorymonitordbus.c ++++ b/gio/gmemorymonitordbus.c +@@ -115,8 +115,7 @@ lmm_vanished_cb (GDBusConnection *connection, + { + GMemoryMonitorDBus *dbus = user_data; + +- if (dbus->proxy != NULL) +- g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + } + +@@ -143,8 +142,7 @@ g_memory_monitor_dbus_finalize (GObject *object) + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object); + +- if (dbus->proxy != NULL) +- g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); + +-- +GitLab + + +From 92399e7114e590f0371b1a5d71f478f840cb4074 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 28 Jul 2021 15:30:15 +0200 +Subject: [PATCH 3/4] gio: Do not block when low-memory-monitor daemon appears + +--- + gio/gmemorymonitordbus.c | 42 +++++++++++++++++++++++++++------------- + 1 file changed, 29 insertions(+), 13 deletions(-) + +diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c +index 08dc53df1..739b83214 100644 +--- a/gio/gmemorymonitordbus.c ++++ b/gio/gmemorymonitordbus.c +@@ -25,6 +25,7 @@ + #include "giomodule-priv.h" + #include "glibintl.h" + #include "glib/gstdio.h" ++#include "gcancellable.h" + #include "gdbusproxy.h" + #include "gdbusnamewatching.h" + +@@ -38,6 +39,7 @@ struct _GMemoryMonitorDBus + GObject parent_instance; + + guint watch_id; ++ GCancellable *cancellable; + GDBusProxy *proxy; + gulong signal_id; + }; +@@ -77,24 +79,15 @@ proxy_signal_cb (GDBusProxy *proxy, + } + + static void +-lmm_appeared_cb (GDBusConnection *connection, +- const gchar *name, +- const gchar *name_owner, +- gpointer user_data) ++lmm_proxy_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) + { + GMemoryMonitorDBus *dbus = user_data; + GDBusProxy *proxy; + GError *error = NULL; + +- proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, +- G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, +- NULL, +- "org.freedesktop.LowMemoryMonitor", +- "/org/freedesktop/LowMemoryMonitor", +- "org.freedesktop.LowMemoryMonitor", +- NULL, +- &error); +- ++ proxy = g_dbus_proxy_new_finish (res, &error); + if (!proxy) + { + g_debug ("Failed to create LowMemoryMonitor D-Bus proxy: %s", +@@ -106,6 +99,26 @@ lmm_appeared_cb (GDBusConnection *connection, + dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal", + G_CALLBACK (proxy_signal_cb), dbus); + dbus->proxy = proxy; ++ ++} ++ ++static void ++lmm_appeared_cb (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ GMemoryMonitorDBus *dbus = user_data; ++ ++ g_dbus_proxy_new (connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ NULL, ++ "org.freedesktop.LowMemoryMonitor", ++ "/org/freedesktop/LowMemoryMonitor", ++ "org.freedesktop.LowMemoryMonitor", ++ dbus->cancellable, ++ lmm_proxy_cb, ++ dbus); + } + + static void +@@ -126,6 +139,7 @@ g_memory_monitor_dbus_initable_init (GInitable *initable, + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (initable); + ++ dbus->cancellable = g_cancellable_new (); + dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, + "org.freedesktop.LowMemoryMonitor", + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, +@@ -142,6 +156,8 @@ g_memory_monitor_dbus_finalize (GObject *object) + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object); + ++ g_cancellable_cancel (dbus->cancellable); ++ g_clear_object (&dbus->cancellable); + g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); +-- +GitLab + + +From 889bdb994fed44344a84ad01aa5633a1b1b62b19 Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Tue, 20 Jul 2021 16:04:31 -0500 +Subject: [PATCH 4/4] Add GPowerProfileMonitor + +--- + docs/reference/gio/gio-docs.xml | 1 + + docs/reference/gio/gio-sections-common.txt | 18 ++ + docs/reference/gio/meson.build | 1 + + gio/gio.h | 1 + + gio/giomodule.c | 7 + + gio/gpowerprofilemonitor.c | 141 ++++++++++++ + gio/gpowerprofilemonitor.h | 63 ++++++ + gio/gpowerprofilemonitordbus.c | 240 +++++++++++++++++++++ + gio/gpowerprofilemonitordbus.h | 32 +++ + gio/meson.build | 3 + + gio/tests/meson.build | 1 + + gio/tests/power-profile-monitor.c | 79 +++++++ + 12 files changed, 587 insertions(+) + create mode 100644 gio/gpowerprofilemonitor.c + create mode 100644 gio/gpowerprofilemonitor.h + create mode 100644 gio/gpowerprofilemonitordbus.c + create mode 100644 gio/gpowerprofilemonitordbus.h + create mode 100644 gio/tests/power-profile-monitor.c + +diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml +index a09d6d31d..b01133900 100644 +--- a/docs/reference/gio/gio-docs.xml ++++ b/docs/reference/gio/gio-docs.xml +@@ -238,6 +238,7 @@ + + + ++ + + + Extending GIO +diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt +index 250491a42..a7addedc2 100644 +--- a/docs/reference/gio/gio-sections-common.txt ++++ b/docs/reference/gio/gio-sections-common.txt +@@ -4247,6 +4247,24 @@ G_NETWORK_MONITOR_GET_INTERFACE + g_network_connectivity_get_type + + ++
++gpowerprofilemonitor ++GPowerProfileMonitor ++GPowerProfileMonitor ++GPowerProfileMonitorInterface ++G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME ++g_power_profile_monitor_dup_default ++g_power_profile_monitor_get_power_saver_enabled ++ ++g_power_profile_monitor_get_type ++G_TYPE_POWER_PROFILE_MONITOR ++G_POWER_PROFILE_MONITOR ++G_IS_POWER_PROFILE_MONITOR ++G_POWER_PROFILE_MONITOR_GET_INTERFACE ++G_TYPE_POWER_PROFILE_LEVEL ++g_power_profile_level_get_type ++
++ +
+ gmenuexporter + g_dbus_connection_export_menu_model +diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build +index 4d0364819..fbabd25ca 100644 +--- a/docs/reference/gio/meson.build ++++ b/docs/reference/gio/meson.build +@@ -65,6 +65,7 @@ if get_option('gtk_doc') + 'gopenuriportal.h', + 'gpollfilemonitor.h', + 'gportalsupport.h', ++ 'gpowerprofilemonitordbus.h', + 'gproxyresolverportal.h', + 'gregistrysettingsbackend.h', + 'gresourcefile.h', +diff --git a/gio/gio.h b/gio/gio.h +index f5d2dd5a3..e9afab666 100644 +--- a/gio/gio.h ++++ b/gio/gio.h +@@ -120,6 +120,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/gio/giomodule.c b/gio/giomodule.c +index c1d451b5c..dfd895717 100644 +--- a/gio/giomodule.c ++++ b/gio/giomodule.c +@@ -48,6 +48,8 @@ + #include "gmemorymonitor.h" + #include "gmemorymonitorportal.h" + #include "gmemorymonitordbus.h" ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitordbus.h" + #ifdef G_OS_WIN32 + #include "gregistrysettingsbackend.h" + #include "giowin32-priv.h" +@@ -1077,6 +1079,7 @@ extern GType _g_network_monitor_nm_get_type (void); + + extern GType g_memory_monitor_dbus_get_type (void); + extern GType g_memory_monitor_portal_get_type (void); ++extern GType g_power_profile_monitor_dbus_get_type (void); + + #ifdef G_OS_UNIX + extern GType g_fdo_notification_backend_get_type (void); +@@ -1187,6 +1190,9 @@ _g_io_modules_ensure_extension_points_registered (void) + + ep = g_io_extension_point_register (G_MEMORY_MONITOR_EXTENSION_POINT_NAME); + g_io_extension_point_set_required_type (ep, G_TYPE_MEMORY_MONITOR); ++ ++ ep = g_io_extension_point_register (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME); ++ g_io_extension_point_set_required_type (ep, G_TYPE_POWER_PROFILE_MONITOR); + } + + G_UNLOCK (registered_extensions); +@@ -1272,6 +1278,7 @@ _g_io_modules_ensure_loaded (void) + g_type_ensure (g_null_settings_backend_get_type ()); + g_type_ensure (g_memory_settings_backend_get_type ()); + g_type_ensure (g_keyfile_settings_backend_get_type ()); ++ g_type_ensure (g_power_profile_monitor_dbus_get_type ()); + #if defined(HAVE_INOTIFY_INIT1) + g_type_ensure (g_inotify_file_monitor_get_type ()); + #endif +diff --git a/gio/gpowerprofilemonitor.c b/gio/gpowerprofilemonitor.c +new file mode 100644 +index 000000000..f5028b3e8 +--- /dev/null ++++ b/gio/gpowerprofilemonitor.c +@@ -0,0 +1,141 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc ++ * Copyright 2021 Igalia S.L. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#include "config.h" ++#include "glib.h" ++#include "glibintl.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "ginetaddress.h" ++#include "ginetsocketaddress.h" ++#include "ginitable.h" ++#include "gioenumtypes.h" ++#include "giomodule-priv.h" ++#include "gtask.h" ++ ++/** ++ * SECTION:gpowerprofilemonitor ++ * @title: GPowerProfileMonitor ++ * @short_description: Power profile monitor ++ * @include: gio/gio.h ++ * ++ * #GPowerProfileMonitor makes it possible for applications as well as OS components ++ * to monitor system power profiles and act upon them. It currently only exports ++ * whether the system is in “Power Saver” mode (known as “Low Power” mode on ++ * some systems). ++ * ++ * When in “Low Power” mode, it is recommended that applications: ++ * - disabling automatic downloads ++ * - reduce the rate of refresh from online sources such as calendar or ++ * email synchronisation ++ * - if the application has expensive visual effects, reduce them ++ * ++ * It is also likely that OS components providing services to applications will ++ * lower their own background activity, for the sake of the system. ++ * ++ * There are a variety of tools that exist for power consumption analysis, but those ++ * usually depend on the OS and hardware used. On Linux, one could use `upower` to ++ * monitor the battery discharge rate, `powertop` to check on the background activity ++ * or activity at all), `sysprof` to inspect CPU usage, and `intel_gpu_time` to ++ * profile GPU usage. ++ * ++ * Don't forget to disconnect the #GPowerProfileMonitor::notify::power-saver-enabled ++ * signal, and unref the #GPowerProfileMonitor itself when exiting. ++ * ++ * Since: 2.70 ++ */ ++ ++/** ++ * GPowerProfileMonitor: ++ * ++ * #GPowerProfileMonitor monitors system power profile and notifies on ++ * changes. ++ * ++ * Since: 2.70 ++ */ ++ ++/** ++ * GPowerProfileMonitorInterface: ++ * @g_iface: The parent interface. ++ * ++ * The virtual function table for #GPowerProfileMonitor. ++ * ++ * Since: 2.70 ++ */ ++ ++G_DEFINE_INTERFACE_WITH_CODE (GPowerProfileMonitor, g_power_profile_monitor, G_TYPE_OBJECT, ++ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE)) ++ ++ ++/** ++ * g_power_profile_monitor_dup_default: ++ * ++ * Gets a reference to the default #GPowerProfileMonitor for the system. ++ * ++ * Returns: (not nullable) (transfer full): a new reference to the default #GPowerProfileMonitor ++ * ++ * Since: 2.70 ++ */ ++GPowerProfileMonitor * ++g_power_profile_monitor_dup_default (void) ++{ ++ return g_object_ref (_g_io_module_get_default (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ "GIO_USE_POWER_PROFILE_MONITOR", ++ NULL)); ++} ++ ++/** ++ * g_power_profile_monitor_get_power_saver_enabled: ++ * @monitor: a #GPowerProfileMonitor ++ * ++ * Gets whether the system is in “Power Saver” mode. ++ * ++ * You are expected to listen to the ++ * #GPowerProfileMonitor::notify::power-saver-enabled signal to know when the profile has ++ * changed. ++ * ++ * Returns: Whether the system is in “Power Saver” mode. ++ * ++ * Since: 2.70 ++ */ ++gboolean ++g_power_profile_monitor_get_power_saver_enabled (GPowerProfileMonitor *monitor) ++{ ++ gboolean enabled; ++ g_object_get (monitor, "power-saver-enabled", &enabled, NULL); ++ return enabled; ++} ++ ++static void ++g_power_profile_monitor_default_init (GPowerProfileMonitorInterface *iface) ++{ ++ /** ++ * GPowerProfileMonitor:power-saver-enabled: ++ * ++ * Whether “Power Saver” mode is enabled on the system. ++ * ++ * Since: 2.70 ++ */ ++ g_object_interface_install_property (iface, ++ g_param_spec_boolean ("power-saver-enabled", ++ "power-saver-enabled", ++ "Power Saver Enabled", ++ FALSE, ++ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); ++} +diff --git a/gio/gpowerprofilemonitor.h b/gio/gpowerprofilemonitor.h +new file mode 100644 +index 000000000..0891fc3dc +--- /dev/null ++++ b/gio/gpowerprofilemonitor.h +@@ -0,0 +1,63 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyright 2021 Igalia S.L. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_H__ ++#define __G_POWER_PROFILE_MONITOR_H__ ++ ++#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) ++#error "Only can be included directly." ++#endif ++ ++#include ++ ++G_BEGIN_DECLS ++ ++/** ++ * G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME: ++ * ++ * Extension point for power profile usage monitoring functionality. ++ * See [Extending GIO][extending-gio]. ++ * ++ * Since: 2.70 ++ */ ++#define G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME "gio-power-profile-monitor" ++ ++#define G_TYPE_POWER_PROFILE_MONITOR (g_power_profile_monitor_get_type ()) ++GLIB_AVAILABLE_IN_2_70 ++G_DECLARE_INTERFACE (GPowerProfileMonitor, g_power_profile_monitor, g, power_profile_monitor, GObject) ++ ++#define G_POWER_PROFILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitor)) ++#define G_IS_POWER_PROFILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_POWER_PROFILE_MONITOR)) ++#define G_POWER_PROFILE_MONITOR_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitorInterface)) ++ ++struct _GPowerProfileMonitorInterface ++{ ++ /*< private >*/ ++ GTypeInterface g_iface; ++}; ++ ++GLIB_AVAILABLE_IN_2_70 ++GPowerProfileMonitor *g_power_profile_monitor_dup_default (void); ++ ++GLIB_AVAILABLE_IN_2_70 ++gboolean g_power_profile_monitor_get_power_saver_enabled (GPowerProfileMonitor *monitor); ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_H__ */ +diff --git a/gio/gpowerprofilemonitordbus.c b/gio/gpowerprofilemonitordbus.c +new file mode 100644 +index 000000000..8bbfe3acc +--- /dev/null ++++ b/gio/gpowerprofilemonitordbus.c +@@ -0,0 +1,240 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyrgith 2021 Igalia S.L. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#include "config.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitordbus.h" ++#include "gioerror.h" ++#include "ginitable.h" ++#include "giomodule-priv.h" ++#include "glibintl.h" ++#include "glib/gstdio.h" ++#include "gcancellable.h" ++#include "gdbusproxy.h" ++#include "gdbusnamewatching.h" ++ ++#define G_POWER_PROFILE_MONITOR_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) ++ ++static void g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *iface); ++static void g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface); ++ ++struct _GPowerProfileMonitorDBus ++{ ++ GObject parent_instance; ++ ++ guint watch_id; ++ GCancellable *cancellable; ++ GDBusProxy *proxy; ++ gulong signal_id; ++ ++ gboolean power_saver_enabled; ++}; ++ ++typedef enum ++{ ++ PROP_POWER_SAVER_ENABLED = 1, ++} GPowerProfileMonitorDBusProperty; ++ ++#define POWERPROFILES_DBUS_NAME "net.hadess.PowerProfiles" ++#define POWERPROFILES_DBUS_IFACE "net.hadess.PowerProfiles" ++#define POWERPROFILES_DBUS_PATH "/net/hadess/PowerProfiles" ++ ++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ++ g_power_profile_monitor_dbus_initable_iface_init) ++ G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR, ++ g_power_profile_monitor_dbus_iface_init) ++ _g_io_modules_ensure_extension_points_registered (); ++ g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ g_define_type_id, ++ "dbus", ++ 30)) ++ ++static void ++g_power_profile_monitor_dbus_init (GPowerProfileMonitorDBus *dbus) ++{ ++ dbus->power_saver_enabled = FALSE; ++} ++ ++static void ++ppd_properties_changed_cb (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv *invalidated_properties, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ const char *active_profile; ++ gboolean enabled; ++ ++ if (!g_variant_lookup (changed_properties, "ActiveProfile", "&s", &active_profile)) ++ return; ++ ++ enabled = g_strcmp0 (active_profile, "power-saver") == 0; ++ if (enabled == dbus->power_saver_enabled) ++ return; ++ ++ dbus->power_saver_enabled = enabled; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++} ++ ++static void ++ppd_proxy_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ GVariant *active_profile_variant; ++ GDBusProxy *proxy; ++ GError *error = NULL; ++ const char *active_profile; ++ gboolean power_saver_enabled; ++ ++ proxy = g_dbus_proxy_new_finish (res, &error); ++ if (!proxy) ++ { ++ g_debug ("GPowerProfileMonitorDBus: Failed to create PowerProfiles D-Bus proxy: %s", ++ error->message); ++ g_error_free (error); ++ return; ++ } ++ ++ active_profile_variant = g_dbus_proxy_get_cached_property (proxy, "ActiveProfile"); ++ if (g_variant_is_of_type (active_profile_variant, G_VARIANT_TYPE_STRING)) ++ { ++ active_profile = g_variant_get_string (active_profile_variant, NULL); ++ power_saver_enabled = g_strcmp0 (active_profile, "power-saver") == 0; ++ if (power_saver_enabled != dbus->power_saver_enabled) ++ { ++ dbus->power_saver_enabled = power_saver_enabled; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++ } ++ } ++ ++ dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed", ++ G_CALLBACK (ppd_properties_changed_cb), dbus); ++ dbus->proxy = g_steal_pointer (&proxy); ++} ++ ++static void ++ppd_appeared_cb (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ ++ g_dbus_proxy_new (connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ NULL, ++ POWERPROFILES_DBUS_NAME, ++ POWERPROFILES_DBUS_PATH, ++ POWERPROFILES_DBUS_IFACE, ++ dbus->cancellable, ++ ppd_proxy_cb, ++ dbus); ++} ++ ++static void ++ppd_vanished_cb (GDBusConnection *connection, ++ const gchar *name, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_object (&dbus->proxy); ++ ++ dbus->power_saver_enabled = FALSE; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_dbus_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object); ++ ++ switch ((GPowerProfileMonitorDBusProperty) prop_id) ++ { ++ case PROP_POWER_SAVER_ENABLED: ++ g_value_set_boolean (value, dbus->power_saver_enabled); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static gboolean ++g_power_profile_monitor_dbus_initable_init (GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (initable); ++ ++ dbus->cancellable = g_cancellable_new (); ++ dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, ++ POWERPROFILES_DBUS_NAME, ++ G_BUS_NAME_WATCHER_FLAGS_AUTO_START, ++ ppd_appeared_cb, ++ ppd_vanished_cb, ++ dbus, ++ NULL); ++ ++ return TRUE; ++} ++ ++static void ++g_power_profile_monitor_dbus_finalize (GObject *object) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object); ++ ++ g_cancellable_cancel (dbus->cancellable); ++ g_clear_object (&dbus->cancellable); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_object (&dbus->proxy); ++ g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); ++ ++ G_OBJECT_CLASS (g_power_profile_monitor_dbus_parent_class)->finalize (object); ++} ++ ++static void ++g_power_profile_monitor_dbus_class_init (GPowerProfileMonitorDBusClass *nl_class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); ++ ++ gobject_class->get_property = g_power_profile_monitor_dbus_get_property; ++ gobject_class->finalize = g_power_profile_monitor_dbus_finalize; ++ ++ g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *monitor_iface) ++{ ++} ++ ++static void ++g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface) ++{ ++ iface->init = g_power_profile_monitor_dbus_initable_init; ++} +diff --git a/gio/gpowerprofilemonitordbus.h b/gio/gpowerprofilemonitordbus.h +new file mode 100644 +index 000000000..ecf7246d1 +--- /dev/null ++++ b/gio/gpowerprofilemonitordbus.h +@@ -0,0 +1,32 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyright 2021 Igalia S.L. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_DBUS_H__ ++#define __G_POWER_PROFILE_MONITOR_DBUS_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_POWER_PROFILE_MONITOR_DBUS (g_power_profile_monitor_dbus_get_type ()) ++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G, POWER_PROFILE_MONITOR_DBUS, GObject) ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_DBUS_H__ */ +diff --git a/gio/meson.build b/gio/meson.build +index 49a37a7bd..d5838ed8a 100644 +--- a/gio/meson.build ++++ b/gio/meson.build +@@ -533,6 +533,8 @@ gio_sources = files( + 'gpollableoutputstream.c', + 'gpollableutils.c', + 'gpollfilemonitor.c', ++ 'gpowerprofilemonitor.c', ++ 'gpowerprofilemonitordbus.c', + 'gproxy.c', + 'gproxyaddress.c', + 'gproxyaddressenumerator.c', +@@ -673,6 +675,7 @@ gio_headers = files( + 'gpollableinputstream.h', + 'gpollableoutputstream.h', + 'gpollableutils.h', ++ 'gpowerprofilemonitor.h', + 'gproxy.h', + 'gproxyaddress.h', + 'gproxyaddressenumerator.h', +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index 98d1401d0..fc2055101 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -75,6 +75,7 @@ gio_tests = { + 'network-monitor-race' : {}, + 'permission' : {}, + 'pollable' : {'dependencies' : [libdl_dep]}, ++ 'power-profile-monitor' : {}, + 'proxy-test' : {}, + 'readwrite' : {}, + 'simple-async-result' : {}, +diff --git a/gio/tests/power-profile-monitor.c b/gio/tests/power-profile-monitor.c +new file mode 100644 +index 000000000..bb32f181f +--- /dev/null ++++ b/gio/tests/power-profile-monitor.c +@@ -0,0 +1,79 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Igalia S.L. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#include ++ ++static void ++test_dup_default (void) ++{ ++ GPowerProfileMonitor *monitor; ++ ++ monitor = g_power_profile_monitor_dup_default (); ++ g_assert_nonnull (monitor); ++ g_object_unref (monitor); ++} ++ ++static void ++power_saver_enabled_cb (GPowerProfileMonitor *monitor, ++ GParamSpec *pspec, ++ gpointer user_data) ++{ ++ gboolean enabled; ++ ++ enabled = g_power_profile_monitor_get_power_saver_enabled (monitor); ++ g_debug ("Power Saver %s (%d)", enabled ? "enabled" : "disabled", enabled); ++} ++ ++static void ++do_watch_power_profile (void) ++{ ++ GPowerProfileMonitor *monitor; ++ GMainLoop *loop; ++ gulong signal_id; ++ ++ monitor = g_power_profile_monitor_dup_default (); ++ signal_id = g_signal_connect (G_OBJECT (monitor), "notify::power-saver-enabled", ++ G_CALLBACK (power_saver_enabled_cb), NULL); ++ ++ loop = g_main_loop_new (NULL, TRUE); ++ g_main_loop_run (loop); ++ ++ g_signal_handler_disconnect (monitor, signal_id); ++ g_object_unref (monitor); ++ g_main_loop_unref (loop); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ if (argc == 2 && !strcmp (argv[1], "--watch")) ++ { ++ do_watch_power_profile (); ++ return 0; ++ } ++ ++ g_test_init (&argc, &argv, NULL); ++ ++ g_test_add_func ("/power-profile-monitor/default", test_dup_default); ++ ++ ret = g_test_run (); ++ ++ return ret; ++} +-- +GitLab + diff --git a/SOURCES/2222.patch b/SOURCES/2222.patch new file mode 100644 index 0000000..0b6106f --- /dev/null +++ b/SOURCES/2222.patch @@ -0,0 +1,739 @@ +From 9645cbffa8ba1a08b73fdae50b31125d11aa5684 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Mon, 9 Aug 2021 23:19:17 +0200 +Subject: [PATCH 1/4] gio: Add portal version of GPowerProfileMonitor + +--- + docs/reference/gio/meson.build | 1 + + gio/giomodule.c | 2 + + gio/gpowerprofilemonitorportal.c | 182 +++++++++++++++++++++++++++++++ + gio/gpowerprofilemonitorportal.h | 31 ++++++ + gio/meson.build | 1 + + 5 files changed, 217 insertions(+) + create mode 100644 gio/gpowerprofilemonitorportal.c + create mode 100644 gio/gpowerprofilemonitorportal.h + +diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build +index fbabd25ca..9aaafeed5 100644 +--- a/docs/reference/gio/meson.build ++++ b/docs/reference/gio/meson.build +@@ -66,6 +66,7 @@ if get_option('gtk_doc') + 'gpollfilemonitor.h', + 'gportalsupport.h', + 'gpowerprofilemonitordbus.h', ++ 'gpowerprofilemonitorportal.h', + 'gproxyresolverportal.h', + 'gregistrysettingsbackend.h', + 'gresourcefile.h', +diff --git a/gio/giomodule.c b/gio/giomodule.c +index dfd895717..d34037a45 100644 +--- a/gio/giomodule.c ++++ b/gio/giomodule.c +@@ -50,6 +50,7 @@ + #include "gmemorymonitordbus.h" + #include "gpowerprofilemonitor.h" + #include "gpowerprofilemonitordbus.h" ++#include "gpowerprofilemonitorportal.h" + #ifdef G_OS_WIN32 + #include "gregistrysettingsbackend.h" + #include "giowin32-priv.h" +@@ -1305,6 +1306,7 @@ _g_io_modules_ensure_loaded (void) + g_type_ensure (g_memory_monitor_dbus_get_type ()); + g_type_ensure (g_memory_monitor_portal_get_type ()); + g_type_ensure (g_network_monitor_portal_get_type ()); ++ g_type_ensure (g_power_profile_monitor_portal_get_type ()); + g_type_ensure (g_proxy_resolver_portal_get_type ()); + #endif + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +diff --git a/gio/gpowerprofilemonitorportal.c b/gio/gpowerprofilemonitorportal.c +new file mode 100644 +index 000000000..bb1b4fd15 +--- /dev/null ++++ b/gio/gpowerprofilemonitorportal.c +@@ -0,0 +1,182 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#include "config.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitorportal.h" ++#include "gdbuserror.h" ++#include "gdbusproxy.h" ++#include "ginitable.h" ++#include "gioerror.h" ++#include "giomodule-priv.h" ++#include "gportalsupport.h" ++ ++#define G_POWER_PROFILE_MONITOR_PORTAL_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) ++ ++static void g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *iface); ++static void g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface); ++ ++typedef enum ++{ ++ PROP_POWER_SAVER_ENABLED = 1, ++} GPowerProfileMonitorPortalProperty; ++ ++struct _GPowerProfileMonitorPortal ++{ ++ GObject parent_instance; ++ ++ GDBusProxy *proxy; ++ gulong signal_id; ++ gboolean power_saver_enabled; ++}; ++ ++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ++ g_power_profile_monitor_portal_initable_iface_init) ++ G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR, ++ g_power_profile_monitor_portal_iface_init) ++ _g_io_modules_ensure_extension_points_registered (); ++ g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ g_define_type_id, ++ "portal", ++ 40)) ++ ++static void ++g_power_profile_monitor_portal_init (GPowerProfileMonitorPortal *portal) ++{ ++} ++ ++static void ++proxy_properties_changed (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorPortal *ppm = user_data; ++ gboolean power_saver_enabled; ++ ++ if (!g_variant_lookup (changed_properties, "power-saver-enabled", "b", &power_saver_enabled)) ++ return; ++ ++ if (power_saver_enabled == ppm->power_saver_enabled) ++ return; ++ ++ ppm->power_saver_enabled = power_saver_enabled; ++ g_object_notify (G_OBJECT (ppm), "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_portal_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object); ++ ++ switch ((GPowerProfileMonitorPortalProperty) prop_id) ++ { ++ case PROP_POWER_SAVER_ENABLED: ++ g_value_set_boolean (value, ppm->power_saver_enabled); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static gboolean ++g_power_profile_monitor_portal_initable_init (GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (initable); ++ GDBusProxy *proxy; ++ gchar *name_owner; ++ ++ if (!glib_should_use_portal ()) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not using portals"); ++ return FALSE; ++ } ++ ++ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ NULL, ++ "org.freedesktop.portal.Desktop", ++ "/org/freedesktop/portal/desktop", ++ "org.freedesktop.portal.PowerProfileMonitor", ++ cancellable, ++ error); ++ if (!proxy) ++ return FALSE; ++ ++ name_owner = g_dbus_proxy_get_name_owner (proxy); ++ ++ if (name_owner == NULL) ++ { ++ g_object_unref (proxy); ++ g_set_error (error, ++ G_DBUS_ERROR, ++ G_DBUS_ERROR_NAME_HAS_NO_OWNER, ++ "Desktop portal not found"); ++ return FALSE; ++ } ++ ++ g_free (name_owner); ++ ++ ppm->signal_id = g_signal_connect (proxy, "g-properties-changed", ++ G_CALLBACK (proxy_properties_changed), ppm); ++ ++ ppm->proxy = g_steal_pointer (&proxy); ++ ++ return TRUE; ++} ++ ++static void ++g_power_profile_monitor_portal_finalize (GObject *object) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object); ++ ++ g_clear_signal_handler (&ppm->signal_id, ppm->proxy); ++ g_clear_object (&ppm->proxy); ++ ++ G_OBJECT_CLASS (g_power_profile_monitor_portal_parent_class)->finalize (object); ++} ++ ++static void ++g_power_profile_monitor_portal_class_init (GPowerProfileMonitorPortalClass *nl_class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); ++ ++ gobject_class->get_property = g_power_profile_monitor_portal_get_property; ++ gobject_class->finalize = g_power_profile_monitor_portal_finalize; ++ ++ g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *monitor_iface) ++{ ++} ++ ++static void ++g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface) ++{ ++ iface->init = g_power_profile_monitor_portal_initable_init; ++} +diff --git a/gio/gpowerprofilemonitorportal.h b/gio/gpowerprofilemonitorportal.h +new file mode 100644 +index 000000000..b91a14610 +--- /dev/null ++++ b/gio/gpowerprofilemonitorportal.h +@@ -0,0 +1,31 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_PORTAL_H__ ++#define __G_POWER_PROFILE_MONITOR_PORTAL_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_POWER_PROFILE_MONITOR_PORTAL (g_power_profile_monitor_portal_get_type ()) ++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G, POWER_PROFILE_MONITOR_PORTAL, GObject) ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_PORTAL_H__ */ +diff --git a/gio/meson.build b/gio/meson.build +index d5838ed8a..ac3373f2b 100644 +--- a/gio/meson.build ++++ b/gio/meson.build +@@ -383,6 +383,7 @@ if host_system != 'windows' + 'gopenuriportal.c', + 'gmemorymonitorportal.c', + 'gnetworkmonitorportal.c', ++ 'gpowerprofilemonitorportal.c', + 'gproxyresolverportal.c', + 'gtrashportal.c', + 'gportalsupport.c', +-- +GitLab + + +From 18eb29897d80bf662d58bd11a89617ddd7ebfeed Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Tue, 10 Aug 2021 10:58:53 +0200 +Subject: [PATCH 2/4] gio: Add GPowerProfileMonitor tests + +Tests both the portal and direct D-Bus variants. +--- + gio/tests/meson.build | 14 ++- + gio/tests/power-profile-monitor-dbus.py.in | 107 ++++++++++++++++ + gio/tests/power-profile-monitor-portal.py.in | 126 +++++++++++++++++++ + 3 files changed, 241 insertions(+), 6 deletions(-) + create mode 100755 gio/tests/power-profile-monitor-dbus.py.in + create mode 100755 gio/tests/power-profile-monitor-portal.py.in + +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index fc2055101..5dbfb8e60 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -541,27 +541,29 @@ if installed_tests_enabled + install_subdir('static-link', install_dir : installed_tests_execdir) + install_data('static-link.py', install_dir : installed_tests_execdir) + +- memory_monitor_tests = [ ++ monitor_tests = [ + 'memory-monitor-dbus', + 'memory-monitor-portal', ++ 'power-profile-monitor-dbus', ++ 'power-profile-monitor-portal' + ] + +- foreach memory_monitor_test : memory_monitor_tests ++ foreach monitor_test : monitor_tests + cdata = configuration_data() + cdata.set('installed_tests_dir', installed_tests_execdir) +- cdata.set('program', memory_monitor_test + '.py') ++ cdata.set('program', monitor_test + '.py') + cdata.set('env', '') + configure_file( + input: installed_tests_template_tap, +- output: memory_monitor_test + '.test', ++ output: monitor_test + '.test', + install_dir: installed_tests_metadir, + configuration: cdata + ) + cdata = configuration_data() + cdata.set('libexecdir', join_paths(glib_prefix, get_option('libexecdir'))) + configure_file( +- input: memory_monitor_test + '.py.in', +- output: memory_monitor_test + '.py', ++ input: monitor_test + '.py.in', ++ output: monitor_test + '.py', + install_dir : installed_tests_execdir, + configuration: cdata, + ) +diff --git a/gio/tests/power-profile-monitor-dbus.py.in b/gio/tests/power-profile-monitor-dbus.py.in +new file mode 100755 +index 000000000..06e594f4a +--- /dev/null ++++ b/gio/tests/power-profile-monitor-dbus.py.in +@@ -0,0 +1,107 @@ ++#!/usr/bin/python3 ++ ++# This program is free software; you can redistribute it and/or modify it under ++# the terms of the GNU Lesser General Public License as published by the Free ++# Software Foundation; either version 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++__author__ = 'Bastien Nocera' ++__email__ = 'hadess@hadess.net' ++__copyright__ = '(c) 2019, 2021 Red Hat Inc.' ++__license__ = 'LGPL 3+' ++ ++import unittest ++import sys ++import subprocess ++import fcntl ++import os ++import time ++ ++import taptestrunner ++ ++try: ++ # Do all non-standard imports here so we can skip the tests if any ++ # needed packages are not available. ++ import dbus ++ import dbus.mainloop.glib ++ import dbusmock ++ from gi.repository import GLib ++ from gi.repository import Gio ++ ++ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) ++ ++ class TestPowerProfileMonitor(dbusmock.DBusTestCase): ++ '''Test GPowerProfileMonitorDBus''' ++ ++ @classmethod ++ def setUpClass(klass): ++ klass.start_system_bus() ++ klass.dbus_con = klass.get_dbus(True) ++ ++ def setUp(self): ++ try: ++ Gio.PowerProfileMonitor ++ except AttributeError: ++ raise unittest.SkipTest('Power Profile Monitor not in ' ++ 'introspection data. Requires ' ++ 'GObject-Introspection ≥ 1.63.2') # FIXME version ++ try: ++ (self.p_mock, self.obj_ppd) = self.spawn_server_template( ++ 'power_profiles_daemon', {}, stdout=subprocess.PIPE) ++ except ModuleNotFoundError: ++ raise unittest.SkipTest("power-profiles-daemon dbusmock template not " ++ "found. Requires dbusmock > 0.23.1.") # FIXME version ++ # set log to nonblocking ++ flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) ++ fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) ++ self.power_saver_enabled = False ++ self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) ++ self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() ++ self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb) ++ self.mainloop = GLib.MainLoop() ++ self.main_context = self.mainloop.get_context() ++ ++ def tearDown(self): ++ self.p_mock.terminate() ++ self.p_mock.wait() ++ ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ ++ def power_saver_enabled_cb(self, spec, data): ++ self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() ++ self.main_context.wakeup() ++ ++ def test_power_profile_power_saver_enabled(self): ++ '''power-saver-enabled property''' ++ ++ self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) ++ ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) ++ ++except ImportError as e: ++ @unittest.skip("Cannot import %s" % e.name) ++ class TestPowerProfileMonitor(unittest.TestCase): ++ def test_power_profile_power_saver_enabled(self): ++ pass ++ ++if __name__ == '__main__': ++ unittest.main(testRunner=taptestrunner.TAPTestRunner()) +diff --git a/gio/tests/power-profile-monitor-portal.py.in b/gio/tests/power-profile-monitor-portal.py.in +new file mode 100755 +index 000000000..960a62232 +--- /dev/null ++++ b/gio/tests/power-profile-monitor-portal.py.in +@@ -0,0 +1,126 @@ ++#!/usr/bin/python3 ++ ++# This program is free software; you can redistribute it and/or modify it under ++# the terms of the GNU Lesser General Public License as published by the Free ++# Software Foundation; either version 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++__author__ = 'Bastien Nocera' ++__email__ = 'hadess@hadess.net' ++__copyright__ = '(c) 2021 Red Hat Inc.' ++__license__ = 'LGPL 3+' ++ ++import unittest ++import sys ++import subprocess ++import fcntl ++import os ++import time ++ ++import taptestrunner ++ ++try: ++ # Do all non-standard imports here so we can skip the tests if any ++ # needed packages are not available. ++ import dbus ++ import dbus.mainloop.glib ++ import dbusmock ++ from gi.repository import GLib ++ from gi.repository import Gio ++ ++ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) ++ ++ # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") ++ XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" ++ ++ class TestPowerProfileMonitorPortal(dbusmock.DBusTestCase): ++ '''Test GPowerProfileMonitorPortal''' ++ ++ @classmethod ++ def setUpClass(klass): ++ klass.start_system_bus() ++ klass.dbus_con = klass.get_dbus(True) ++ # Start session bus so that xdg-desktop-portal can run on it ++ klass.start_session_bus() ++ ++ def setUp(self): ++ try: ++ Gio.PowerProfileMonitor ++ except AttributeError: ++ raise unittest.SkipTest('Power Profile Monitor not in ' ++ 'introspection data. Requires ' ++ 'GObject-Introspection > 1.69.0') ++ try: ++ (self.p_mock, self.obj_ppd) = self.spawn_server_template( ++ 'power_profiles_daemon', {}, stdout=subprocess.PIPE) ++ except ModuleNotFoundError: ++ raise unittest.SkipTest("power-profiles-daemon dbusmock template not " ++ "found. Requires dbusmock > 0.23.1.") ++ # set log to nonblocking ++ flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) ++ fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) ++ self.power_saver_enabled = False ++ self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) ++ try: ++ self.xdp = subprocess.Popen([XDG_DESKTOP_PORTAL_PATH]) ++ except FileNotFoundError: ++ raise unittest.SkipTest("xdg-desktop-portal not available") ++ ++ try: ++ self.wait_for_bus_object('org.freedesktop.portal.Desktop', ++ '/org/freedesktop/portal/desktop') ++ except: ++ raise ++ # subprocess.Popen(['gdbus', 'monitor', '--session', '--dest', 'org.freedesktop.portal.Desktop']) ++ ++ os.environ['GTK_USE_PORTAL'] = "1" ++ self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() ++ assert("GPowerProfileMonitorPortal" in str(self.power_profile_monitor)) ++ self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb) ++ self.mainloop = GLib.MainLoop() ++ self.main_context = self.mainloop.get_context() ++ ++ def tearDown(self): ++ self.p_mock.terminate() ++ self.p_mock.wait() ++ ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ ++ def power_saver_enabled_cb(self, spec, data): ++ self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() ++ self.main_context.wakeup() ++ ++ def test_power_profile_power_saver_enabled_portal(self): ++ '''power-saver-enabled property''' ++ ++ self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) ++ ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) ++ ++except ImportError as e: ++ @unittest.skip("Cannot import %s" % e.name) ++ class TestPowerProfileMonitorPortal(unittest.TestCase): ++ def test_power_profile_power_saver_enabled_portal(self): ++ pass ++ ++if __name__ == '__main__': ++ unittest.main(testRunner=taptestrunner.TAPTestRunner()) +-- +GitLab + + +From 66acea8418eb3d8e46bb6f93dc0c3f13a1f7822b Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 11 Aug 2021 15:37:40 +0200 +Subject: [PATCH 3/4] gio: Remove left-over debug statement from memory monitor + portal test + +--- + gio/tests/memory-monitor-portal.py.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in +index cb4a960eb..f5fd2283f 100755 +--- a/gio/tests/memory-monitor-portal.py.in ++++ b/gio/tests/memory-monitor-portal.py.in +@@ -31,7 +31,6 @@ try: + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +- # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") + XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" + + class TestLowMemoryMonitorPortal(dbusmock.DBusTestCase): +-- +GitLab + + +From 2e9842cafc73a7fb94cfde7937e125e1a91f35f8 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 11 Aug 2021 15:38:12 +0200 +Subject: [PATCH 4/4] gio: Simplify memory monitor tests by using + assertEventually() helper + +assertEventually is a helper used in a number of projects that use +dbusmock. + +See https://github.com/martinpitt/python-dbusmock/issues/82 +--- + gio/tests/memory-monitor-dbus.py.in | 31 ++++++++++++++++----------- + gio/tests/memory-monitor-portal.py.in | 31 ++++++++++++++++----------- + 2 files changed, 38 insertions(+), 24 deletions(-) + +diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in +index 7823e7309..e8ac28faf 100755 +--- a/gio/tests/memory-monitor-dbus.py.in ++++ b/gio/tests/memory-monitor-dbus.py.in +@@ -66,6 +66,23 @@ try: + self.p_mock.terminate() + self.p_mock.wait() + ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ + def memory_warning_cb(self, monitor, level): + self.last_warning = level + self.main_context.wakeup() +@@ -82,21 +99,11 @@ try: + + self.dbusmock.EmitWarning(100) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 100: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 100) ++ self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20) + + self.dbusmock.EmitWarning(255) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 255: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 255) ++ self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20) + + except ImportError as e: + @unittest.skip("Cannot import %s" % e.name) +diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in +index f5fd2283f..36d5094d3 100755 +--- a/gio/tests/memory-monitor-portal.py.in ++++ b/gio/tests/memory-monitor-portal.py.in +@@ -84,6 +84,23 @@ try: + self.p_mock.terminate() + self.p_mock.wait() + ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ + def portal_memory_warning_cb(self, monitor, level): + self.last_warning = level + self.main_context.wakeup() +@@ -100,21 +117,11 @@ try: + + self.dbusmock.EmitWarning(100) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 100: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 100) ++ self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20) + + self.dbusmock.EmitWarning(255) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 255: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 255) ++ self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20) + + except ImportError as e: + @unittest.skip("Cannot import %s" % e.name) +-- +GitLab + diff --git a/SOURCES/2244.patch b/SOURCES/2244.patch new file mode 100644 index 0000000..cb55031 --- /dev/null +++ b/SOURCES/2244.patch @@ -0,0 +1,49 @@ +From b6036e23b0477be147211b4e21a6b49cd4d6c9a0 Mon Sep 17 00:00:00 2001 +From: Jamie Bainbridge +Date: Wed, 8 Sep 2021 12:08:17 +1000 +Subject: [PATCH] gutils: Avoid segfault in g_get_user_database_entry + +g_get_user_database_entry() uses variable pwd to store the contents of +the call to getpwnam_r(), then capitalises the first letter of pw_name +with g_ascii_toupper (pw->pw_name[0]). + +However, as per the getpwnam manpage, the result of that call "may point +to a static area". When this happens, GLib is trying to edit static +memory which belongs to a shared library, so segfaults. + +Instead, copy pw_name off to a temporary variable, set uppercase on +that variable, and use the variable to join into the desired string. +Free the new variable after it is no longer needed. + +Signed-off-by: Jamie Bainbridge +--- + glib/gutils.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/glib/gutils.c b/glib/gutils.c +index b7a2113d4..4bccd7229 100644 +--- a/glib/gutils.c ++++ b/glib/gutils.c +@@ -692,14 +692,17 @@ g_get_user_database_entry (void) + { + gchar **gecos_fields; + gchar **name_parts; ++ gchar *uppercase_pw_name; + + /* split the gecos field and substitute '&' */ + gecos_fields = g_strsplit (pw->pw_gecos, ",", 0); + name_parts = g_strsplit (gecos_fields[0], "&", 0); +- pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]); +- e.real_name = g_strjoinv (pw->pw_name, name_parts); ++ uppercase_pw_name = g_strdup (pw->pw_name); ++ uppercase_pw_name[0] = g_ascii_toupper (uppercase_pw_name[0]); ++ e.real_name = g_strjoinv (uppercase_pw_name, name_parts); + g_strfreev (gecos_fields); + g_strfreev (name_parts); ++ g_free (uppercase_pw_name); + } + #endif + +-- +GitLab + diff --git a/SOURCES/2291.patch b/SOURCES/2291.patch new file mode 100644 index 0000000..2e4e44d --- /dev/null +++ b/SOURCES/2291.patch @@ -0,0 +1,129 @@ +From f419966808475cb6c0f0ba2f63967876218ffdaf Mon Sep 17 00:00:00 2001 +From: Julian Andres Klode +Date: Tue, 12 Oct 2021 12:01:50 +0200 +Subject: [PATCH 1/2] gnetworkmonitornm: Stop using removed PropertiesChanged + signal + +Use the org.freedesktop.DBus.Properties interface to listen +to PropertiesChanged signals on /org/freedesktop/NetworkManager. + +NetworkManager used to provide its own legacy PropertiesChanged +signal, but that was dropped in +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/853 + +This requires NetworkManager >= 1.2 (2016) + +Fixes: #2505 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1946196 +--- + gio/gnetworkmonitornm.c | 29 +++++++---------------------- + 1 file changed, 7 insertions(+), 22 deletions(-) + +diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c +index 5a36a0ba1..6a6d1d666 100644 +--- a/gio/gnetworkmonitornm.c ++++ b/gio/gnetworkmonitornm.c +@@ -267,29 +267,14 @@ update_cached_property (GDBusProxy *proxy, + } + + static void +-proxy_signal_cb (GDBusProxy *proxy, +- const gchar *sender_name, +- const gchar *signal_name, +- GVariant *parameters, +- GNetworkMonitorNM *nm) ++proxy_properties_changed_cb (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ GNetworkMonitorNM *nm) + { +- GVariant *asv; + GVariantDict *dict; + +- if (g_strcmp0 (signal_name, "PropertiesChanged") != 0) +- return; +- +- g_variant_get (parameters, "(@a{sv})", &asv); +- if (!asv) +- return; +- +- dict = g_variant_dict_new (asv); +- g_variant_unref (asv); +- if (!dict) +- { +- g_warning ("Failed to handle PropertiesChanged signal from NetworkManager"); +- return; +- } ++ dict = g_variant_dict_new (changed_properties); + + update_cached_property (nm->priv->proxy, "Connectivity", dict); + +@@ -361,8 +346,8 @@ g_network_monitor_nm_initable_init (GInitable *initable, + return FALSE; + } + +- nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal", +- G_CALLBACK (proxy_signal_cb), nm); ++ nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed", ++ G_CALLBACK (proxy_properties_changed_cb), nm); + nm->priv->proxy = proxy; + sync_properties (nm, FALSE); + +-- +GitLab + + +From 643fc7ea49e818310f6b3f6e4ebe621c7a4d6bd7 Mon Sep 17 00:00:00 2001 +From: Julian Andres Klode +Date: Tue, 12 Oct 2021 17:31:42 +0200 +Subject: [PATCH 2/2] gnetworkmonitornm: Do not re-update cached property + +GDBusProxy already takes care of updating the cached property +before emitting the signal, so there is no need to do this +a second time ourselves. +--- + gio/gnetworkmonitornm.c | 22 ---------------------- + 1 file changed, 22 deletions(-) + +diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c +index 6a6d1d666..a8040fb36 100644 +--- a/gio/gnetworkmonitornm.c ++++ b/gio/gnetworkmonitornm.c +@@ -252,34 +252,12 @@ sync_properties (GNetworkMonitorNM *nm, + } + } + +-static void +-update_cached_property (GDBusProxy *proxy, +- const char *property_name, +- GVariantDict *dict) +-{ +- GVariant *v; +- +- v = g_variant_dict_lookup_value (dict, property_name, NULL); +- if (!v) +- return; +- g_dbus_proxy_set_cached_property (proxy, property_name, v); +- g_variant_unref (v); +-} +- + static void + proxy_properties_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + GNetworkMonitorNM *nm) + { +- GVariantDict *dict; +- +- dict = g_variant_dict_new (changed_properties); +- +- update_cached_property (nm->priv->proxy, "Connectivity", dict); +- +- g_variant_dict_unref (dict); +- + sync_properties (nm, TRUE); + } + +-- +GitLab + diff --git a/SOURCES/2435.patch b/SOURCES/2435.patch new file mode 100644 index 0000000..862503b --- /dev/null +++ b/SOURCES/2435.patch @@ -0,0 +1,132 @@ +From a879d08e912a4421786b44af479f94f7b4503f5a Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 17 Jan 2022 15:27:24 +0000 +Subject: [PATCH] gspawn: Report errors with closing file descriptors between + fork/exec +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If a seccomp policy is set up incorrectly so that it returns `EPERM` for +`close_range()` rather than `ENOSYS` due to it not being recognised, no +error would previously be reported from GLib, but some file descriptors +wouldn’t be closed, and that would cause a hung zombie process. The +zombie process would be waiting for one half of a socket to be closed. + +Fix that by correctly propagating errors from `close_range()` back to the +parent process so they can be reported correctly. + +Distributions which aren’t yet carrying the Docker fix to correctly +return `ENOSYS` from unrecognised syscalls may want to temporarily carry +an additional patch to fall back to `safe_fdwalk()` if `close_range()` +fails with `EPERM`. This change will not be accepted upstream as `EPERM` +is not the right error for `close_range()` to be returning. + +Signed-off-by: Philip Withnall + +Fixes: #2580 +--- + glib/gspawn.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index c2fe306dc..9c2f7ba7b 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1457,8 +1457,10 @@ safe_fdwalk (int (*cb)(void *data, int fd), void *data) + } + + /* This function is called between fork() and exec() and hence must be +- * async-signal-safe (see signal-safety(7)). */ +-static void ++ * async-signal-safe (see signal-safety(7)). ++ * ++ * On failure, `-1` will be returned and errno will be set. */ ++static int + safe_closefrom (int lowfd) + { + #if defined(__FreeBSD__) || defined(__OpenBSD__) +@@ -1472,6 +1474,7 @@ safe_closefrom (int lowfd) + * should be safe to use. + */ + (void) closefrom (lowfd); ++ return 0; + #elif defined(__DragonFly__) + /* It is unclear whether closefrom function included in DragonFlyBSD libc_r + * is safe to use because it calls a lot of library functions. It is also +@@ -1479,12 +1482,13 @@ safe_closefrom (int lowfd) + * direct system call here ourselves to avoid possible issues. + */ + (void) syscall (SYS_closefrom, lowfd); ++ return 0; + #elif defined(F_CLOSEM) + /* NetBSD and AIX have a special fcntl command which does the same thing as + * closefrom. NetBSD also includes closefrom function, which seems to be a + * simple wrapper of the fcntl command. + */ +- (void) fcntl (lowfd, F_CLOSEM); ++ return fcntl (lowfd, F_CLOSEM); + #else + + #if defined(HAVE_CLOSE_RANGE) +@@ -1494,9 +1498,11 @@ safe_closefrom (int lowfd) + * + * Handle ENOSYS in case it’s supported in libc but not the kernel; if so, + * fall back to safe_fdwalk(). */ +- if (close_range (lowfd, G_MAXUINT, 0) != 0 && errno == ENOSYS) ++ int ret = close_range (lowfd, G_MAXUINT, 0); ++ if (ret == 0 || errno != ENOSYS) ++ return ret; + #endif /* HAVE_CLOSE_RANGE */ +- (void) safe_fdwalk (close_func, GINT_TO_POINTER (lowfd)); ++ return safe_fdwalk (close_func, GINT_TO_POINTER (lowfd)); + #endif + } + +@@ -1534,7 +1540,8 @@ enum + CHILD_EXEC_FAILED, + CHILD_OPEN_FAILED, + CHILD_DUP2_FAILED, +- CHILD_FORK_FAILED ++ CHILD_FORK_FAILED, ++ CHILD_CLOSE_FAILED, + }; + + /* This function is called between fork() and exec() and hence must be +@@ -1650,12 +1657,14 @@ do_exec (gint child_err_report_fd, + if (safe_dup2 (child_err_report_fd, 3) < 0) + write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + set_cloexec (GINT_TO_POINTER (0), 3); +- safe_closefrom (4); ++ if (safe_closefrom (4) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED); + child_err_report_fd = 3; + } + else + { +- safe_fdwalk (set_cloexec, GINT_TO_POINTER (3)); ++ if (safe_fdwalk (set_cloexec, GINT_TO_POINTER (3)) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED); + } + } + else +@@ -2446,7 +2455,15 @@ fork_exec (gboolean intermediate_child, + _("Failed to fork child process (%s)"), + g_strerror (buf[1])); + break; +- ++ ++ case CHILD_CLOSE_FAILED: ++ g_set_error (error, ++ G_SPAWN_ERROR, ++ G_SPAWN_ERROR_FAILED, ++ _("Failed to close file descriptor for child process (%s)"), ++ g_strerror (buf[1])); ++ break; ++ + default: + g_set_error (error, + G_SPAWN_ERROR, +-- +2.34.1 + diff --git a/SOURCES/gnutls-hmac.patch b/SOURCES/gnutls-hmac.patch new file mode 100644 index 0000000..4b1ba0e --- /dev/null +++ b/SOURCES/gnutls-hmac.patch @@ -0,0 +1,1086 @@ +From ff90bb8474b1e724727f4014b446e7c851e609bd Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 18:44:43 +0000 +Subject: [PATCH 1/4] ghmac: Split off wrapper functions into ghmac-utils.c + +Prep for adding a GnuTLS HMAC implementation; these are just +utility functions that call the "core" API. +--- + glib/ghmac-utils.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ + glib/ghmac.c | 112 ---------------------------------- + glib/meson.build | 1 + + 3 files changed, 146 insertions(+), 112 deletions(-) + create mode 100644 glib/ghmac-utils.c + +diff --git a/glib/ghmac-utils.c b/glib/ghmac-utils.c +new file mode 100644 +index 000000000..a17359ff1 +--- /dev/null ++++ b/glib/ghmac-utils.c +@@ -0,0 +1,145 @@ ++/* ghmac.h - data hashing functions ++ * ++ * Copyright (C) 2011 Collabora Ltd. ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this library; if not, see . ++ */ ++ ++#include "config.h" ++ ++#include ++ ++#include "ghmac.h" ++ ++#include "glib/galloca.h" ++#include "gatomic.h" ++#include "gslice.h" ++#include "gmem.h" ++#include "gstrfuncs.h" ++#include "gtestutils.h" ++#include "gtypes.h" ++#include "glibintl.h" ++ ++/** ++ * g_compute_hmac_for_data: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: (array length=key_len): the key to use in the HMAC ++ * @key_len: the length of the key ++ * @data: (array length=length): binary blob to compute the HMAC of ++ * @length: length of @data ++ * ++ * Computes the HMAC for a binary @data of @length. This is a ++ * convenience wrapper for g_hmac_new(), g_hmac_get_string() ++ * and g_hmac_unref(). ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC of the binary data as a string in hexadecimal. ++ * The returned string should be freed with g_free() when done using it. ++ * ++ * Since: 2.30 ++ */ ++gchar * ++g_compute_hmac_for_data (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len, ++ const guchar *data, ++ gsize length) ++{ ++ GHmac *hmac; ++ gchar *retval; ++ ++ g_return_val_if_fail (length == 0 || data != NULL, NULL); ++ ++ hmac = g_hmac_new (digest_type, key, key_len); ++ if (!hmac) ++ return NULL; ++ ++ g_hmac_update (hmac, data, length); ++ retval = g_strdup (g_hmac_get_string (hmac)); ++ g_hmac_unref (hmac); ++ ++ return retval; ++} ++ ++/** ++ * g_compute_hmac_for_bytes: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: the key to use in the HMAC ++ * @data: binary blob to compute the HMAC of ++ * ++ * Computes the HMAC for a binary @data. This is a ++ * convenience wrapper for g_hmac_new(), g_hmac_get_string() ++ * and g_hmac_unref(). ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC of the binary data as a string in hexadecimal. ++ * The returned string should be freed with g_free() when done using it. ++ * ++ * Since: 2.50 ++ */ ++gchar * ++g_compute_hmac_for_bytes (GChecksumType digest_type, ++ GBytes *key, ++ GBytes *data) ++{ ++ gconstpointer byte_data; ++ gsize length; ++ gconstpointer key_data; ++ gsize key_len; ++ ++ g_return_val_if_fail (data != NULL, NULL); ++ g_return_val_if_fail (key != NULL, NULL); ++ ++ byte_data = g_bytes_get_data (data, &length); ++ key_data = g_bytes_get_data (key, &key_len); ++ return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); ++} ++ ++ ++/** ++ * g_compute_hmac_for_string: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: (array length=key_len): the key to use in the HMAC ++ * @key_len: the length of the key ++ * @str: the string to compute the HMAC for ++ * @length: the length of the string, or -1 if the string is nul-terminated ++ * ++ * Computes the HMAC for a string. ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC as a hexadecimal string. ++ * The returned string should be freed with g_free() ++ * when done using it. ++ * ++ * Since: 2.30 ++ */ ++gchar * ++g_compute_hmac_for_string (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len, ++ const gchar *str, ++ gssize length) ++{ ++ g_return_val_if_fail (length == 0 || str != NULL, NULL); ++ ++ if (length < 0) ++ length = strlen (str); ++ ++ return g_compute_hmac_for_data (digest_type, key, key_len, ++ (const guchar *) str, length); ++} +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 49fd272f0..4f181f21f 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -329,115 +329,3 @@ g_hmac_get_digest (GHmac *hmac, + g_checksum_update (hmac->digesto, buffer, len); + g_checksum_get_digest (hmac->digesto, buffer, digest_len); + } +- +-/** +- * g_compute_hmac_for_data: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: (array length=key_len): the key to use in the HMAC +- * @key_len: the length of the key +- * @data: (array length=length): binary blob to compute the HMAC of +- * @length: length of @data +- * +- * Computes the HMAC for a binary @data of @length. This is a +- * convenience wrapper for g_hmac_new(), g_hmac_get_string() +- * and g_hmac_unref(). +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC of the binary data as a string in hexadecimal. +- * The returned string should be freed with g_free() when done using it. +- * +- * Since: 2.30 +- */ +-gchar * +-g_compute_hmac_for_data (GChecksumType digest_type, +- const guchar *key, +- gsize key_len, +- const guchar *data, +- gsize length) +-{ +- GHmac *hmac; +- gchar *retval; +- +- g_return_val_if_fail (length == 0 || data != NULL, NULL); +- +- hmac = g_hmac_new (digest_type, key, key_len); +- if (!hmac) +- return NULL; +- +- g_hmac_update (hmac, data, length); +- retval = g_strdup (g_hmac_get_string (hmac)); +- g_hmac_unref (hmac); +- +- return retval; +-} +- +-/** +- * g_compute_hmac_for_bytes: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: the key to use in the HMAC +- * @data: binary blob to compute the HMAC of +- * +- * Computes the HMAC for a binary @data. This is a +- * convenience wrapper for g_hmac_new(), g_hmac_get_string() +- * and g_hmac_unref(). +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC of the binary data as a string in hexadecimal. +- * The returned string should be freed with g_free() when done using it. +- * +- * Since: 2.50 +- */ +-gchar * +-g_compute_hmac_for_bytes (GChecksumType digest_type, +- GBytes *key, +- GBytes *data) +-{ +- gconstpointer byte_data; +- gsize length; +- gconstpointer key_data; +- gsize key_len; +- +- g_return_val_if_fail (data != NULL, NULL); +- g_return_val_if_fail (key != NULL, NULL); +- +- byte_data = g_bytes_get_data (data, &length); +- key_data = g_bytes_get_data (key, &key_len); +- return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); +-} +- +- +-/** +- * g_compute_hmac_for_string: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: (array length=key_len): the key to use in the HMAC +- * @key_len: the length of the key +- * @str: the string to compute the HMAC for +- * @length: the length of the string, or -1 if the string is nul-terminated +- * +- * Computes the HMAC for a string. +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC as a hexadecimal string. +- * The returned string should be freed with g_free() +- * when done using it. +- * +- * Since: 2.30 +- */ +-gchar * +-g_compute_hmac_for_string (GChecksumType digest_type, +- const guchar *key, +- gsize key_len, +- const gchar *str, +- gssize length) +-{ +- g_return_val_if_fail (length == 0 || str != NULL, NULL); +- +- if (length < 0) +- length = strlen (str); +- +- return g_compute_hmac_for_data (digest_type, key, key_len, +- (const guchar *) str, length); +-} +diff --git a/glib/meson.build b/glib/meson.build +index 8c18e6de4..329b8d197 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -253,6 +253,7 @@ glib_sources = files( + 'ggettext.c', + 'ghash.c', + 'ghmac.c', ++ 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', + 'giochannel.c', +-- +2.31.1 + +From 5395d36e6685e0b7377794c59c5820970bb472ef Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 19:36:54 +0000 +Subject: [PATCH 2/4] Add a gnutls backend for GHmac + +For RHEL we want apps to use FIPS-certified crypto libraries, +and HMAC apparently counts as "keyed" and hence needs to +be validated. + +Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1630260 +Replaces: https://gitlab.gnome.org/GNOME/glib/merge_requests/897 + +This is a build-time option that backs the GHmac API with GnuTLS. +Most distributors ship glib-networking built with GnuTLS, and +most apps use glib-networking, so this isn't a net-new library +in most cases. + +======================================================================= + +mcatanzaro note: + +I've updated Colin's original patch with several enhancements: + +Implement g_hmac_copy() using gnutls_hmac_copy(), which didn't exist +when Colin developed this patch. + +Removed use of GSlice + +Better error checking in g_hmac_new(). It is possible for +gnutls_hmac_init() to fail if running in FIPS mode and an MD5 digest is +requested. In this case, we should return NULL rather than returning a +broken GHmac with a NULL gnutls_hmac_hd_t. This was leading to a later +null pointer dereference inside gnutls_hmac_update(). Applications are +responsible for checking to ensure the return value of g_hmac_new() is +not NULL since it is annotated as nullable. Added documentation to +indicate this possibility. + +Properly handle length -1 in g_hmac_update(). This means we've been +given a NUL-terminated string and should use strlen(). GnuTLS doesn't +accept -1, so let's call strlen() ourselves. + +Crash the application with g_error() if gnutls_hmac() fails for any +reason. This is necessary because g_hmac_update() is not fallible, so we +have no way to indicate error. Crashing seems better than returning the +wrong result later when g_hmac_get_string() or g_hmac_get_digest() is +later called. (Those functions are also not fallible.) Fortunately, I +don't think this error should actually be hit in practice. + +https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 +--- + glib/gchecksum.c | 9 +- + glib/gchecksumprivate.h | 32 +++++++ + glib/ghmac-gnutls.c | 187 ++++++++++++++++++++++++++++++++++++++++ + glib/ghmac.c | 15 ++++ + glib/meson.build | 10 ++- + meson.build | 7 ++ + meson_options.txt | 7 +- + 7 files changed, 260 insertions(+), 7 deletions(-) + create mode 100644 glib/gchecksumprivate.h + create mode 100644 glib/ghmac-gnutls.c + +diff --git a/glib/gchecksum.c b/glib/gchecksum.c +index 29b479bc6..929958c3a 100644 +--- a/glib/gchecksum.c ++++ b/glib/gchecksum.c +@@ -20,7 +20,7 @@ + + #include + +-#include "gchecksum.h" ++#include "gchecksumprivate.h" + + #include "gslice.h" + #include "gmem.h" +@@ -173,9 +173,9 @@ sha_byte_reverse (guint32 *buffer, + } + #endif /* G_BYTE_ORDER == G_BIG_ENDIAN */ + +-static gchar * +-digest_to_string (guint8 *digest, +- gsize digest_len) ++gchar * ++gchecksum_digest_to_string (guint8 *digest, ++ gsize digest_len) + { + gsize i, len = digest_len * 2; + gchar *retval; +@@ -194,6 +194,7 @@ digest_to_string (guint8 *digest, + + return retval; + } ++#define digest_to_string gchecksum_digest_to_string + + /* + * MD5 Checksum +diff --git a/glib/gchecksumprivate.h b/glib/gchecksumprivate.h +new file mode 100644 +index 000000000..86c7a3b61 +--- /dev/null ++++ b/glib/gchecksumprivate.h +@@ -0,0 +1,32 @@ ++/* gstdioprivate.h - Private GLib stdio functions ++ * ++ * Copyright 2017 Руслан Ижбулатов ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this library; if not, see . ++ */ ++ ++#ifndef __G_CHECKSUMPRIVATE_H__ ++#define __G_CHECKSUMPRIVATE_H__ ++ ++#include "gchecksum.h" ++ ++G_BEGIN_DECLS ++ ++gchar * ++gchecksum_digest_to_string (guint8 *digest, ++ gsize digest_len); ++ ++G_END_DECLS ++ ++#endif +\ No newline at end of file +diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c +new file mode 100644 +index 000000000..9fb775f89 +--- /dev/null ++++ b/glib/ghmac-gnutls.c +@@ -0,0 +1,187 @@ ++/* ghmac.h - data hashing functions ++ * ++ * Copyright (C) 2011 Collabora Ltd. ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this library; if not, see . ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "ghmac.h" ++ ++#include "glib/galloca.h" ++#include "gatomic.h" ++#include "gslice.h" ++#include "gmem.h" ++#include "gstrfuncs.h" ++#include "gchecksumprivate.h" ++#include "gtestutils.h" ++#include "gtypes.h" ++#include "glibintl.h" ++ ++#ifndef HAVE_GNUTLS ++#error "build configuration error" ++#endif ++ ++struct _GHmac ++{ ++ int ref_count; ++ GChecksumType digest_type; ++ gnutls_hmac_hd_t hmac; ++ gchar *digest_str; ++}; ++ ++GHmac * ++g_hmac_new (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len) ++{ ++ gnutls_mac_algorithm_t algo; ++ GHmac *hmac = g_new0 (GHmac, 1); ++ int ret; ++ ++ hmac->ref_count = 1; ++ hmac->digest_type = digest_type; ++ ++ switch (digest_type) ++ { ++ case G_CHECKSUM_MD5: ++ algo = GNUTLS_MAC_MD5; ++ break; ++ case G_CHECKSUM_SHA1: ++ algo = GNUTLS_MAC_SHA1; ++ break; ++ case G_CHECKSUM_SHA256: ++ algo = GNUTLS_MAC_SHA256; ++ break; ++ case G_CHECKSUM_SHA384: ++ algo = GNUTLS_MAC_SHA384; ++ break; ++ case G_CHECKSUM_SHA512: ++ algo = GNUTLS_MAC_SHA512; ++ break; ++ default: ++ g_free (hmac); ++ g_return_val_if_reached (NULL); ++ } ++ ++ ret = gnutls_hmac_init (&hmac->hmac, algo, key, key_len); ++ if (ret != 0) ++ { ++ /* There is no way to report an error here, but one possible cause of ++ * failure is that the requested digest may be disabled by FIPS mode. ++ */ ++ g_free (hmac); ++ return NULL; ++ } ++ ++ return hmac; ++} ++ ++GHmac * ++g_hmac_copy (const GHmac *hmac) ++{ ++ GHmac *copy; ++ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ copy = g_new0 (GHmac, 1); ++ copy->ref_count = 1; ++ copy->digest_type = hmac->digest_type; ++ copy->hmac = gnutls_hmac_copy (hmac->hmac); ++ ++ /* g_hmac_copy is not allowed to fail, so we'll have to crash on error. */ ++ if (!copy->hmac) ++ g_error ("gnutls_hmac_copy failed"); ++ ++ return copy; ++} ++ ++GHmac * ++g_hmac_ref (GHmac *hmac) ++{ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ g_atomic_int_inc (&hmac->ref_count); ++ ++ return hmac; ++} ++ ++void ++g_hmac_unref (GHmac *hmac) ++{ ++ g_return_if_fail (hmac != NULL); ++ ++ if (g_atomic_int_dec_and_test (&hmac->ref_count)) ++ { ++ gnutls_hmac_deinit (hmac->hmac, NULL); ++ g_free (hmac->digest_str); ++ g_free (hmac); ++ } ++} ++ ++ ++void ++g_hmac_update (GHmac *hmac, ++ const guchar *data, ++ gssize length) ++{ ++ int ret; ++ ++ g_return_if_fail (hmac != NULL); ++ g_return_if_fail (length == 0 || data != NULL); ++ ++ if (length == -1) ++ length = strlen ((const char *)data); ++ ++ /* g_hmac_update is not allowed to fail, so we'll have to crash on error. */ ++ ret = gnutls_hmac (hmac->hmac, data, length); ++ if (ret != 0) ++ g_error ("gnutls_hmac failed: %s", gnutls_strerror (ret)); ++} ++ ++const gchar * ++g_hmac_get_string (GHmac *hmac) ++{ ++ guint8 *buffer; ++ gsize digest_len; ++ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ if (hmac->digest_str) ++ return hmac->digest_str; ++ ++ digest_len = g_checksum_type_get_length (hmac->digest_type); ++ buffer = g_alloca (digest_len); ++ ++ gnutls_hmac_output (hmac->hmac, buffer); ++ hmac->digest_str = gchecksum_digest_to_string (buffer, digest_len); ++ return hmac->digest_str; ++} ++ ++ ++void ++g_hmac_get_digest (GHmac *hmac, ++ guint8 *buffer, ++ gsize *digest_len) ++{ ++ g_return_if_fail (hmac != NULL); ++ ++ gnutls_hmac_output (hmac->hmac, buffer); ++ *digest_len = g_checksum_type_get_length (hmac->digest_type); ++} +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 4f181f21f..0e39ea40a 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -33,6 +33,9 @@ + #include "gtypes.h" + #include "glibintl.h" + ++#ifdef HAVE_GNUTLS ++#error "build configuration error" ++#endif + + /** + * SECTION:hmac +@@ -84,6 +87,18 @@ struct _GHmac + * Support for digests of type %G_CHECKSUM_SHA512 has been added in GLib 2.42. + * Support for %G_CHECKSUM_SHA384 was added in GLib 2.52. + * ++ * Note that #GHmac creation may fail, in which case this function will ++ * return %NULL. Since there is no error parameter, it is not possible ++ * to indicate why. ++ * ++ * In Fedora, CentOS Stream, and Red Hat Enterprise Linux, GLib is ++ * configured to use GnuTLS to implement #GHmac in order to support FIPS ++ * compliance. This introduces additional failure possibilities that are ++ * not present in upstream GLib. For example, the creation of a #GHmac ++ * will fail if @digest_type is %G_CHECKSUM_MD5 and the system is ++ * running in FIPS mode. #GHmac creation may also fail if GLib is unable ++ * to load GnuTLS. ++ * + * Returns: the newly created #GHmac, or %NULL. + * Use g_hmac_unref() to free the memory allocated by it. + * +diff --git a/glib/meson.build b/glib/meson.build +index 329b8d197..2417de53d 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -252,7 +252,6 @@ glib_sources = files( + 'gfileutils.c', + 'ggettext.c', + 'ghash.c', +- 'ghmac.c', + 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', +@@ -308,6 +307,7 @@ glib_sources = files( + 'guriprivate.h', + 'gutils.c', + 'gutilsprivate.h', ++ 'gchecksumprivate.h', + 'guuid.c', + 'gvariant.c', + 'gvariant-core.c', +@@ -352,6 +352,12 @@ else + glib_dtrace_hdr = [] + endif + ++if get_option('gnutls') ++ glib_sources += files('ghmac-gnutls.c') ++else ++ glib_sources += files('ghmac.c') ++endif ++ + pcre_static_args = [] + + if use_pcre_static_flag +@@ -378,7 +384,7 @@ libglib = library('glib-2.0', + # intl.lib is not compatible with SAFESEH + link_args : [noseh_link_args, glib_link_flags, win32_ldflags], + include_directories : configinc, +- dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], ++ dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], + c_args : glib_c_args, + objc_args : glib_c_args, + ) +diff --git a/meson.build b/meson.build +index e2eba1871..cca15f653 100644 +--- a/meson.build ++++ b/meson.build +@@ -2090,6 +2090,13 @@ if host_system == 'linux' + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) + endif + ++# gnutls is used optionally by ghmac ++libgnutls_dep = [] ++if get_option('gnutls') ++ libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] ++ glib_conf.set('HAVE_GNUTLS', 1) ++endif ++ + if host_system == 'windows' + winsock2 = cc.find_library('ws2_32') + endif +diff --git a/meson_options.txt b/meson_options.txt +index 072765361..c8f26ac02 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -39,6 +39,11 @@ option('internal_pcre', + value : false, + description : 'whether to use internal PCRE') + ++option('gnutls', ++ type : 'boolean', ++ value : false, ++ description : 'build with gnutls support') ++ + option('man', + type : 'boolean', + value : false, +-- +2.31.1 + +From 61c175277acb8d1e080305acd444201c5ad1fb81 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 16 Jun 2021 20:35:00 -0500 +Subject: [PATCH 3/4] dlopen GnuTLS instead of linking directly + +I'd like to enable our GnuTLS GHmac patchset in Fedora in order to +ensure it is receiving sufficient real-world testing, since we've +discovered several bugs thus far. Problem is Fedora has one requirement +that RHEL does not: it needs to build glib as a static lib. This is +needed by QEMU in Fedora for complicated technical reasons that I don't +understand. However, nothing in RHEL needs it. This means we failed to +notice that glib2-static is broken in RHEL, because there is no +gnutls-static! We could fix this by adding a gnutls-static package, but +that seems like overkill, and adding more static libraries where they're +not truly necessary is not the direction we want to move in anyway. So +instead, let's just dlopen GnuTLS to sidestep this problem entirely. + +This would not be a good solution for upstream, but upstream has made +clear that this patchset is already non-upstreamable, so it will be fine +for our purposes. +--- + glib/ghmac-gnutls.c | 101 ++++++++++++++++++++++++++++++++++++++++++-- + glib/ghmac.c | 2 +- + glib/meson.build | 2 +- + meson.build | 6 +-- + 4 files changed, 102 insertions(+), 9 deletions(-) + +diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c +index 9fb775f89..1800fc2e0 100644 +--- a/glib/ghmac-gnutls.c ++++ b/glib/ghmac-gnutls.c +@@ -19,8 +19,8 @@ + + #include "config.h" + ++#include + #include +-#include + + #include "ghmac.h" + +@@ -31,13 +31,16 @@ + #include "gstrfuncs.h" + #include "gchecksumprivate.h" + #include "gtestutils.h" ++#include "gthread.h" + #include "gtypes.h" + #include "glibintl.h" + +-#ifndef HAVE_GNUTLS ++#ifndef USE_GNUTLS + #error "build configuration error" + #endif + ++typedef gpointer gnutls_hmac_hd_t; ++ + struct _GHmac + { + int ref_count; +@@ -46,15 +49,107 @@ struct _GHmac + gchar *digest_str; + }; + ++typedef enum ++{ ++ GNUTLS_MAC_MD5 = 2, ++ GNUTLS_MAC_SHA1 = 3, ++ GNUTLS_MAC_SHA256 = 6, ++ GNUTLS_MAC_SHA384 = 7, ++ GNUTLS_MAC_SHA512 = 8, ++} gnutls_mac_algorithm_t; ++ ++/* Why are we dlopening GnuTLS instead of linking to it directly? Because we ++ * want to be able to build GLib as a static library without depending on a ++ * static build of GnuTLS. QEMU depends on static linking with GLib, but Fedora ++ * does not ship a static build of GnuTLS, and this allows us to avoid changing ++ * that. ++ */ ++static int (*gnutls_hmac_init) (gnutls_hmac_hd_t *dig, gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen); ++static gnutls_hmac_hd_t (*gnutls_hmac_copy) (gnutls_hmac_hd_t handle); ++static void (*gnutls_hmac_deinit) (gnutls_hmac_hd_t handle, void *digest); ++static int (*gnutls_hmac) (gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len); ++static void (*gnutls_hmac_output) (gnutls_hmac_hd_t handle, void *digest); ++static const char * (*gnutls_strerror) (int error); ++ ++static gsize gnutls_initialize_attempted = 0; ++static gboolean gnutls_initialize_successful = FALSE; ++ ++static void ++initialize_gnutls (void) ++{ ++ gpointer libgnutls; ++ ++ libgnutls = dlopen ("libgnutls.so.30", RTLD_LAZY | RTLD_GLOBAL); ++ if (!libgnutls) ++ { ++ g_warning ("Cannot use GHmac: failed to load libgnutls.so.30: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_init = dlsym (libgnutls, "gnutls_hmac_init"); ++ if (!gnutls_hmac_init) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_init: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_copy = dlsym (libgnutls, "gnutls_hmac_copy"); ++ if (!gnutls_hmac_copy) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_copy: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_deinit = dlsym (libgnutls, "gnutls_hmac_deinit"); ++ if (!gnutls_hmac_deinit) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_deinit: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac = dlsym (libgnutls, "gnutls_hmac"); ++ if (!gnutls_hmac) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_output = dlsym (libgnutls, "gnutls_hmac_output"); ++ if (!gnutls_hmac_output) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_output: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_strerror = dlsym (libgnutls, "gnutls_strerror"); ++ if (!gnutls_strerror) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_strerror: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_initialize_successful = TRUE; ++} ++ + GHmac * + g_hmac_new (GChecksumType digest_type, + const guchar *key, + gsize key_len) + { + gnutls_mac_algorithm_t algo; +- GHmac *hmac = g_new0 (GHmac, 1); ++ GHmac *hmac; + int ret; + ++ if (g_once_init_enter (&gnutls_initialize_attempted)) ++ { ++ initialize_gnutls (); ++ g_once_init_leave (&gnutls_initialize_attempted, 1); ++ } ++ ++ if (!gnutls_initialize_successful) ++ return NULL; ++ ++ hmac = g_new0 (GHmac, 1); + hmac->ref_count = 1; + hmac->digest_type = digest_type; + +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 0e39ea40a..2d9be91b8 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -33,7 +33,7 @@ + #include "gtypes.h" + #include "glibintl.h" + +-#ifdef HAVE_GNUTLS ++#ifdef USE_GNUTLS + #error "build configuration error" + #endif + +diff --git a/glib/meson.build b/glib/meson.build +index 2417de53d..ba42951aa 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -384,7 +384,7 @@ libglib = library('glib-2.0', + # intl.lib is not compatible with SAFESEH + link_args : [noseh_link_args, glib_link_flags, win32_ldflags], + include_directories : configinc, +- dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], ++ dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep] + [libdl_dep], + c_args : glib_c_args, + objc_args : glib_c_args, + ) +diff --git a/meson.build b/meson.build +index cca15f653..404ef1790 100644 +--- a/meson.build ++++ b/meson.build +@@ -2090,11 +2090,9 @@ if host_system == 'linux' + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) + endif + +-# gnutls is used optionally by ghmac +-libgnutls_dep = [] ++# gnutls is used optionally by GHmac + if get_option('gnutls') +- libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] +- glib_conf.set('HAVE_GNUTLS', 1) ++ glib_conf.set('USE_GNUTLS', 1) + endif + + if host_system == 'windows' +-- +2.31.1 + +From 7d1d96311b6ecd4f90ebbdd6fc58d28e06a86887 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 16 Jun 2021 20:46:24 -0500 +Subject: [PATCH 4/4] Add test for GHmac in FIPS mode + +This will test a few problems that we hit recently: + +g_hmac_copy() is broken, https://bugzilla.redhat.com/show_bug.cgi?id=1786538 + +Crash in g_hmac_update() in FIPS mode, https://bugzilla.redhat.com/show_bug.cgi?id=1971533 + +Crash when passing -1 length to g_hmac_update() (discovered in #1971533) + +We'll also test to ensure MD5 fails, and stop compiling the other MD5 +tests. +--- + glib/tests/hmac.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 46 insertions(+) + +diff --git a/glib/tests/hmac.c b/glib/tests/hmac.c +index 3ac3206df..2fa447984 100644 +--- a/glib/tests/hmac.c ++++ b/glib/tests/hmac.c +@@ -1,7 +1,10 @@ ++#include "config.h" ++ + #include + #include + #include + ++#ifndef USE_GNUTLS + /* HMAC-MD5 test vectors as per RFC 2202 */ + + /* Test 1 */ +@@ -81,6 +84,7 @@ guint8 key_md5_test7[] = { + guint8 result_md5_test7[] = { + 0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee, 0x1f, 0xb1, + 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e }; ++#endif + + /* HMAC-SHA1, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512 test vectors + * as per RFCs 2202 and 4868. +@@ -299,6 +303,7 @@ typedef struct { + gconstpointer result; + } HmacCase; + ++#ifndef USE_GNUTLS + HmacCase hmac_md5_tests[] = { + { G_CHECKSUM_MD5, key_md5_test1, 16, "Hi There", 8, result_md5_test1 }, + { G_CHECKSUM_MD5, "Jefe", 4, "what do ya want for nothing?", 28, +@@ -317,6 +322,7 @@ HmacCase hmac_md5_tests[] = { + 73, result_md5_test7 }, + { -1, NULL, 0, NULL, 0, NULL }, + }; ++#endif + + HmacCase hmac_sha1_tests[] = { + { G_CHECKSUM_SHA1, key_sha_test1, 20, "Hi There", 8, result_sha1_test1 }, +@@ -493,11 +499,45 @@ test_hmac_for_bytes (void) + g_bytes_unref (data); + } + ++#ifdef USE_GNUTLS ++static void ++test_gnutls_fips_mode (void) ++{ ++ GHmac *hmac; ++ GHmac *copy; ++ ++ /* No MD5 in FIPS mode. */ ++ hmac = g_hmac_new (G_CHECKSUM_MD5, "abc123", sizeof ("abc123")); ++ g_assert_null (hmac); ++ ++ /* SHA-256 should be good. */ ++ hmac = g_hmac_new (G_CHECKSUM_SHA256, "abc123", sizeof ("abc123")); ++ g_assert_nonnull (hmac); ++ ++ /* Ensure g_hmac_update() does not crash when called with -1. */ ++ g_hmac_update (hmac, "You win again, gravity!", -1); ++ ++ /* Ensure g_hmac_copy() does not crash. */ ++ copy = g_hmac_copy (hmac); ++ g_assert_nonnull (hmac); ++ g_hmac_unref (hmac); ++ ++ g_assert_cmpstr (g_hmac_get_string (copy), ==, "795ba6900bcb22e8ce65c2ec02db4e85697da921deb960ee3143bf88a4a60f83"); ++ g_hmac_unref (copy); ++} ++#endif ++ + int + main (int argc, + char **argv) + { + int i; ++ ++#ifdef USE_GNUTLS ++ /* This has to happen before GnuTLS is dlopened. */ ++ g_setenv ("GNUTLS_FORCE_FIPS_MODE", "1", FALSE); ++#endif ++ + g_test_init (&argc, &argv, NULL); + + for (i = 0 ; hmac_sha1_tests[i].key_len > 0 ; i++) +@@ -532,6 +572,7 @@ main (int argc, + g_free (name); + } + ++#ifndef USE_GNUTLS + for (i = 0 ; hmac_md5_tests[i].key_len > 0 ; i++) + { + gchar *name = g_strdup_printf ("/hmac/md5-%d", i + 1); +@@ -539,6 +580,7 @@ main (int argc, + (void (*)(const void *)) test_hmac); + g_free (name); + } ++#endif + + g_test_add_func ("/hmac/ref-unref", test_hmac_ref_unref); + g_test_add_func ("/hmac/copy", test_hmac_copy); +@@ -546,5 +588,9 @@ main (int argc, + g_test_add_func ("/hmac/for-string", test_hmac_for_string); + g_test_add_func ("/hmac/for-bytes", test_hmac_for_bytes); + ++#ifdef USE_GNUTLS ++ g_test_add_func ("/hmac/gnutls-fips-mode", test_gnutls_fips_mode); ++#endif ++ + return g_test_run (); + } +-- +2.31.1 diff --git a/SPECS/glib2.spec b/SPECS/glib2.spec new file mode 100644 index 0000000..42547df --- /dev/null +++ b/SPECS/glib2.spec @@ -0,0 +1,985 @@ +Name: glib2 +Version: 2.68.4 +Release: 5%{?dist} +Summary: A library of handy utility functions + +License: LGPLv2+ +URL: http://www.gtk.org +Source0: http://download.gnome.org/sources/glib/2.68/glib-%{version}.tar.xz + +# Required for RHEL core crypto components policy. Good for Fedora too. +# https://bugzilla.redhat.com/show_bug.cgi?id=1630260 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 +Patch0: gnutls-hmac.patch + +# Add patches to move applications into systemd scopes in compliance with +# https://systemd.io/DESKTOP_ENVIRONMENTS/ +# Proposed upstream at https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1596 +Patch1: 1596.patch + +# Add GPowerProfileMonitor +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1965 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2194 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2222 +Patch2: 1965.patch +Patch3: 2194.patch +Patch4: 2222.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2244 +Patch5: 2244.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2291 +Patch6: 2291.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1968 +Patch7: 1968.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2435 +Patch8: 2435.patch + +BuildRequires: chrpath +BuildRequires: gcc +BuildRequires: gcc-c++ +BuildRequires: gettext +BuildRequires: gtk-doc +BuildRequires: perl-interpreter +# for sys/inotify.h +BuildRequires: glibc-devel +BuildRequires: libattr-devel +BuildRequires: libselinux-devel +BuildRequires: meson +# for sys/sdt.h +BuildRequires: systemtap-sdt-devel +BuildRequires: pkgconfig(libelf) +BuildRequires: pkgconfig(libffi) +BuildRequires: pkgconfig(libpcre) +BuildRequires: pkgconfig(mount) +BuildRequires: pkgconfig(sysprof-capture-4) +BuildRequires: pkgconfig(zlib) +BuildRequires: python3-devel + +# For gnutls-hmac.patch. We now dlopen libgnutls.so.30 so that we can build a +# static glib2 without depending on a static build of GnuTLS as well. This will +# ensure we notice if the GnuTLS soname bumps, so that we can update our patch. +%if 0%{?__isa_bits} == 64 +Requires: libgnutls.so.30()(64bit) +%else +Requires: libgnutls.so.30 +%endif + +# Remove gamin dependency +Obsoletes: glib2-fam < 2.67.1-3 + +# glib 2.59.0 hash table changes broke older gcr versions / password prompts in gnome-shell +Conflicts: gcr < 3.28.1 + +Provides: bundled(gnulib) +Provides: bundled(gvdb) +Provides: bundled(libcharset) +Provides: bundled(xdgmime) + +%description +GLib is the low-level core library that forms the basis for projects +such as GTK+ and GNOME. It provides data structure handling for C, +portability wrappers, and interfaces for such runtime functionality +as an event loop, threads, dynamic loading, and an object system. + + +%package devel +Summary: A library of handy utility functions +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The glib2-devel package includes the header files for the GLib library. + +%package doc +Summary: A library of handy utility functions +Requires: %{name} = %{version}-%{release} +BuildArch: noarch + +%description doc +The glib2-doc package includes documentation for the GLib library. + +%package static +Summary: glib static +Requires: %{name}-devel = %{version}-%{release} + +%description static +The %{name}-static subpackage contains static libraries for %{name}. + +%package tests +Summary: Tests for the glib2 package +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description tests +The glib2-tests package contains tests that can be used to verify +the functionality of the installed glib2 package. + +%prep +%autosetup -n glib-%{version} -p1 + +%build +# Bug 1324770: Also explicitly remove PCRE sources since we use --with-pcre=system +rm glib/pcre/*.[ch] + +%meson \ + -Dman=true \ + -Ddtrace=true \ + -Dsystemtap=true \ + -Dsysprof=enabled \ + -Dglib_debug=disabled \ + -Dgtk_doc=true \ + -Dinstalled_tests=true \ + -Dgnutls=true \ + --default-library=both \ + %{nil} + +%meson_build + +%install +%meson_install +# Since this is a generated .py file, set it to a known timestamp for +# better reproducibility. +# Also copy the timestamp for other .py files, because meson doesn't +# do this, see https://github.com/mesonbuild/meson/issues/5027. +touch -r gio/gdbus-2.0/codegen/config.py.in %{buildroot}%{_datadir}/glib-2.0/codegen/*.py +chrpath --delete %{buildroot}%{_libdir}/*.so + +# Perform byte compilation manually to avoid issues with +# irreproducibility of the default invalidation mode, see +# https://www.python.org/dev/peps/pep-0552/ and +# https://bugzilla.redhat.com/show_bug.cgi?id=1686078 +export PYTHONHASHSEED=0 +%py_byte_compile %{__python3} %{buildroot}%{_datadir} + +mv %{buildroot}%{_bindir}/gio-querymodules %{buildroot}%{_bindir}/gio-querymodules-%{__isa_bits} +sed -i -e "/^gio_querymodules=/s/gio-querymodules/gio-querymodules-%{__isa_bits}/" %{buildroot}%{_libdir}/pkgconfig/gio-2.0.pc + +mkdir -p %{buildroot}%{_libdir}/gio/modules +touch %{buildroot}%{_libdir}/gio/modules/giomodule.cache + +%find_lang glib20 + +%transfiletriggerin -- %{_libdir}/gio/modules +gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : + +%transfiletriggerpostun -- %{_libdir}/gio/modules +gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : + +%transfiletriggerin -- %{_datadir}/glib-2.0/schemas +glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : + +%transfiletriggerpostun -- %{_datadir}/glib-2.0/schemas +glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : + +%files -f glib20.lang +%license COPYING +%doc AUTHORS NEWS README +%{_libdir}/libglib-2.0.so.* +%{_libdir}/libgthread-2.0.so.* +%{_libdir}/libgmodule-2.0.so.* +%{_libdir}/libgobject-2.0.so.* +%{_libdir}/libgio-2.0.so.* +%dir %{_datadir}/bash-completion +%dir %{_datadir}/bash-completion/completions +%{_datadir}/bash-completion/completions/gapplication +%{_datadir}/bash-completion/completions/gdbus +%{_datadir}/bash-completion/completions/gio +%{_datadir}/bash-completion/completions/gsettings +%dir %{_datadir}/glib-2.0 +%dir %{_datadir}/glib-2.0/schemas +%dir %{_libdir}/gio +%dir %{_libdir}/gio/modules +%ghost %{_libdir}/gio/modules/giomodule.cache +%{_bindir}/gio +%{_bindir}/gio-querymodules* +%{_bindir}/glib-compile-schemas +%{_bindir}/gsettings +%{_bindir}/gdbus +%{_bindir}/gapplication +%{_mandir}/man1/gio.1* +%{_mandir}/man1/gio-querymodules.1* +%{_mandir}/man1/glib-compile-schemas.1* +%{_mandir}/man1/gsettings.1* +%{_mandir}/man1/gdbus.1* +%{_mandir}/man1/gapplication.1* + +%files devel +%{_libdir}/lib*.so +%{_libdir}/glib-2.0 +%{_includedir}/* +%{_datadir}/aclocal/* +%{_libdir}/pkgconfig/* +%{_datadir}/glib-2.0/gdb +%{_datadir}/glib-2.0/gettext +%{_datadir}/glib-2.0/schemas/gschema.dtd +%{_datadir}/glib-2.0/valgrind/glib.supp +%{_datadir}/bash-completion/completions/gresource +%{_bindir}/glib-genmarshal +%{_bindir}/glib-gettextize +%{_bindir}/glib-mkenums +%{_bindir}/gobject-query +%{_bindir}/gtester +%{_bindir}/gdbus-codegen +%{_bindir}/glib-compile-resources +%{_bindir}/gresource +%{_datadir}/glib-2.0/codegen +%attr (0755, root, root) %{_bindir}/gtester-report +%{_mandir}/man1/glib-genmarshal.1* +%{_mandir}/man1/glib-gettextize.1* +%{_mandir}/man1/glib-mkenums.1* +%{_mandir}/man1/gobject-query.1* +%{_mandir}/man1/gtester-report.1* +%{_mandir}/man1/gtester.1* +%{_mandir}/man1/gdbus-codegen.1* +%{_mandir}/man1/glib-compile-resources.1* +%{_mandir}/man1/gresource.1* +%{_datadir}/gdb/ +%{_datadir}/gettext/ +%{_datadir}/systemtap/ + +%files doc +%{_datadir}/gtk-doc/ + +%files static +%{_libdir}/libgio-2.0.a +%{_libdir}/libglib-2.0.a +%{_libdir}/libgmodule-2.0.a +%{_libdir}/libgobject-2.0.a +%{_libdir}/libgthread-2.0.a + +%files tests +%{_libexecdir}/installed-tests +%{_datadir}/installed-tests + +%changelog +* Fri Jan 21 2022 Michael Catanzaro - 2.68.4-5 +- Add one more upstream patch to gspawn patchset +- Related: #1910092 + +* Fri Jan 21 2022 Michael Catanzaro - 2.68.4-4 +- Add gspawn patchset +- Resolves: #1910092 + +* Wed Dec 01 2021 Michael Catanzaro - 2.68.4-3 +- Fix GNetworkMonitor after NetworkManager D-Bus API changes +- Resolves: #2014624 + +* Wed Sep 15 2021 Michael Catanzaro - 2.68.4-2 +- Fix g_get_user_database_entry() crash when used with nss-systemd +- Resolves: #2004711 + +* Sat Aug 21 2021 Kalev Lember - 2.68.4-1 +- Update to 2.68.4 + +* Wed Aug 18 2021 DJ Delorie - 2.68.3-6 +- Rebuilt for libffi 3.4.2 SONAME transition. + Related: rhbz#1891914 + +* Tue Aug 17 2021 Michael Catanzaro - 2.68.3-5 +- Backport GPowerProfileMonitor +- Resolves: #1994466 + +* Mon Aug 09 2021 Mohan Boddu - 2.68.3-4 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Tue Jul 27 2021 Michael Catanzaro - 2.68.3-3 +- Fix build with glibc 2.34 +- Resolves: #1984626 + +* Thu Jul 01 2021 Michael Catanzaro - 2.68.3-2 +- Refresh gnutls-hmac patchset to fix leaks in error path +- Related: #1971823 + +* Mon Jun 28 2021 Michael Catanzaro - 2.68.3-1 +- Update to 2.68.3 +- Resolves: #1976713 +- Remove Recommends: shared-mime-info +- Resolves: #1947897 + +* Wed Jun 23 2021 Michael Catanzaro - 2.68.2-2 +- Update GHmac patchset and reenable glib2-static +- Resolves: #1971823 + +* Wed May 19 2021 Michael Catanzaro - 2.68.2-1 +- Update to 2.68.2 +- Resolves: #1961039 + +* Tue May 11 2021 Michael Catanzaro - 2.68.1-4 +- No changes, bump revision to retry gating +- Related: #1951126 + +* Fri May 07 2021 Michael Catanzaro - 2.68.1-3 +- Add missing bundled provides +- Add rpminspect gating configuration +- Consolidate GDesktopAppInfo patchset +- Resolves: #1951126 + +* Wed Apr 28 2021 Michael Catanzaro - 2.68.1-2 +- Refresh GDesktopAppInfo patchset +- Related: #1951126 + +* Thu Apr 22 2021 Kalev Lember - 2.68.1-1 +- Update to 2.68.1 + +* Thu Apr 15 2021 Mohan Boddu - 2.68.0-3 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Fri Mar 26 2021 Kalev Lember - 2.68.0-2 +- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it + +* Thu Mar 18 2021 Kalev Lember - 2.68.0-1 +- Update to 2.68.0 + +* Thu Mar 18 2021 Petr Pisar - 2.67.6-2 +- Disable debugging glib (bug #1936339) + +* Thu Mar 11 2021 Kalev Lember - 2.67.6-1 +- Update to 2.67.6 + +* Tue Mar 02 2021 Kalev Lember - 2.67.5-1 +- Update to 2.67.5 + +* Wed Feb 24 2021 Kalev Lember - 2.67.4-3 +- Enable sysprof capture support + +* Fri Feb 19 2021 Kalev Lember - 2.67.4-2 +- Backport a fix for gsubprocesslauncher regression + +* Tue Feb 16 2021 Kalev Lember - 2.67.4-1 +- Update to 2.67.4 + +* Tue Feb 09 2021 Benjamin Berg - 2.67.3-2 +- Add patches to move applications into systemd scopes + +* Thu Feb 04 2021 Kalev Lember - 2.67.3-1 +- Update to 2.67.3 +- Fix gtk-doc directory ownership + +* Tue Jan 26 2021 Fedora Release Engineering - 2.67.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Jan 22 2021 Peter Robinson - 2.67.1-3 +- Drop dependency on gamin + +* Sat Dec 19 2020 Kevin Fenzi - 2.67.1-2 +- Add already upstream patch to fix gdm crasher. + +* Sat Dec 19 2020 Kalev Lember - 2.67.1-1 +- Update to 2.67.1 + +* Fri Dec 04 2020 Ondrej Holy - 2.67.0-7 +- Explicitly create modules dir to fix ELN build + +* Tue Dec 01 2020 Ondrej Holy and Michael Catanzaro - 2.67.0-6 +- Disable glib2-fam in RHEL + +* Tue Nov 24 2020 Kalev Lember - 2.67.0-5 +- Backport upstream patches to fix invalid use of volatile objects + (gcc 11 support) + +* Wed Nov 11 2020 Michael Catanzaro - 2.67.0-4 +- Make GnuTLS patch RHEL-specific, and make glib2-static subpackage Fedora-specific + +* Tue Nov 10 2020 Michael Catanzaro - 2.67.0-3 +- Use GnuTLS to implement GHmac (thanks to Colin Walters) + +* Wed Nov 04 2020 Michael Catanzaro - 2.67.0-2 +- Backport fix for GSocketClient crash + +* Thu Oct 29 2020 Kalev Lember - 2.67.0-1 +- Update to 2.67.0 + +* Mon Oct 19 2020 Kalev Lember - 2.66.2-1 +- Update to 2.66.2 +- Drop gtk-doc patch as we finally have a new enough gtk-doc + +* Wed Oct 14 2020 Michael Catanzaro - 2.66.1-3 +- Fix yet another timezone bug + +* Wed Oct 14 2020 Michael Catanzaro - 2.66.1-2 +- Fix timezone-related bugs in many applications caused by new glib timezone cache + +* Thu Oct 1 2020 Kalev Lember - 2.66.1-1 +- Update to 2.66.1 + +* Thu Sep 10 2020 Kalev Lember - 2.66.0-1 +- Update to 2.66.0 + +* Wed Sep 02 2020 Kalev Lember - 2.65.3-1 +- Update to 2.65.3 + +* Tue Aug 25 2020 Adam Williamson - 2.65.2-3 +- Backport fix for GGO #2189 (error accessing some filesystems) + +* Thu Aug 20 2020 Jeff Law - 2.65.2-2 +- Re-enable LTO + +* Tue Aug 18 2020 Kalev Lember - 2.65.2-1 +- Update to 2.65.2 + +* Mon Aug 17 2020 Kalev Lember - 2.65.1-1 +- Update to 2.65.1 + +* Sat Aug 01 2020 Fedora Release Engineering - 2.65.0-5 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 2.65.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jun 30 2020 Jeff Law - 2.65.0-3 +Disable LTO + +* Mon Jun 22 2020 Kalev Lember - 2.65.0-2 +- Update gio-2.0.pc with correct gio-querymodules name when renaming it + (#1849441) + +* Mon Jun 22 2020 Kalev Lember - 2.65.0-1 +- Update to 2.65.0 + +* Wed May 20 2020 Kalev Lember - 2.64.3-1 +- Update to 2.64.3 + +* Tue Apr 28 2020 Tomas Popela - 2.64.2-2 +- Backport fix for a race condition in GCancellable (rhbz#1825230) + +* Fri Apr 10 2020 Kalev Lember - 2.64.2-1 +- Update to 2.64.2 + +* Wed Mar 11 2020 Kalev Lember - 2.64.1-1 +- Update to 2.64.1 + +* Mon Mar 02 2020 Kalev Lember - 2.64.0-1 +- Update to 2.64.0 + +* Mon Feb 24 2020 Kalev Lember - 2.63.6-1 +- Update to 2.63.6 + +* Wed Feb 12 2020 Kalev Lember - 2.63.5-3 +- Backport a patch to work around SELinux policies not allowing + SYS_sched_setattr (#1795524) + +* Fri Feb 07 2020 Michael Catanzaro - 2.63.5-2 +- Add patch for CVE-2020-6750 and related issues. + +* Mon Feb 03 2020 Kalev Lember - 2.63.5-1 +- Update to 2.63.5 + +* Wed Jan 29 2020 Stephen Gallagher - 2.63.4-3 +- Fix GThreadPool initialization that is breaking createrepo_c (BZ #1795052) + +* Tue Jan 28 2020 Fedora Release Engineering - 2.63.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Fri Jan 24 2020 Kalev Lember - 2.63.4-1 +- Update to 2.63.4 + +* Mon Dec 16 2019 Kalev Lember - 2.63.3-1 +- Update to 2.63.3 + +* Mon Dec 02 2019 Kalev Lember - 2.63.2-1 +- Update to 2.63.2 + +* Fri Oct 04 2019 Kalev Lember - 2.63.0-1 +- Update to 2.63.0 + +* Fri Oct 04 2019 Kalev Lember - 2.62.1-1 +- Update to 2.62.1 + +* Fri Sep 06 2019 Kalev Lember - 2.62.0-1 +- Update to 2.62.0 + +* Tue Sep 03 2019 Kalev Lember - 2.61.3-1 +- Update to 2.61.3 + +* Mon Aug 12 2019 Kalev Lember - 2.61.2-1 +- Update to 2.61.2 + +* Thu Jul 25 2019 Fedora Release Engineering - 2.61.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jun 11 2019 David King - 2.61.1-2 +- Fix CVE-2019-12450 (#1719142) +- Consistently use buildroot macro + +* Fri May 24 2019 Kalev Lember - 2.61.1-1 +- Update to 2.61.1 + +* Tue Apr 16 2019 Adam Williamson - 2.61.0-2 +- Rebuild with Meson fix for #1699099 + +* Mon Apr 15 2019 Kalev Lember - 2.61.0-1 +- Update to 2.61.0 + +* Mon Apr 15 2019 Kalev Lember - 2.60.1-1 +- Update to 2.60.1 + +* Wed Mar 13 2019 Zbigniew Jędrzejewski-Szmek - 2.60.0-3 +- Switch back to timestamp-based pyc invalidation mode + +* Wed Mar 6 2019 Zbigniew Jędrzejewski-Szmek - 2.60.0-2 +- Make sure all .py files have fixed timestamps (fixes issue with + parallel installability of i686 and amd64 -devel packages) +- Switch to explicit byte compilation to override invalidation mode + +* Mon Mar 04 2019 Kalev Lember - 2.60.0-1 +- Update to 2.60.0 + +* Mon Feb 18 2019 Kalev Lember - 2.59.3-1 +- Update to 2.59.3 + +* Mon Feb 04 2019 Kalev Lember - 2.59.2-1 +- Update to 2.59.2 + +* Thu Jan 31 2019 Fedora Release Engineering - 2.59.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Sat Jan 26 2019 Kalev Lember - 2.59.1-1 +- Update to 2.59.1 + +* Thu Jan 03 2019 Kalev Lember - 2.59.0-1 +- Update to 2.59.0 +- Switch to the meson build system + +* Tue Dec 18 2018 Kalev Lember - 2.58.2-1 +- Update to 2.58.2 + +* Fri Oct 05 2018 Kalev Lember - 2.58.1-2 +- Fix multilib -devel installs (#1634778) + +* Fri Sep 21 2018 Kalev Lember - 2.58.1-1 +- Update to 2.58.1 + +* Wed Sep 05 2018 Kalev Lember - 2.58.0-1 +- Update to 2.58.0 + +* Thu Aug 2 2018 Ondrej Holy - 2.57.2-1 +- Update to 2.57.2 + +* Fri Jul 20 2018 Ondrej Holy - 2.57.1-1 +- Update to 2.57.1 + +* Fri Jul 13 2018 Fedora Release Engineering - 2.56.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jun 19 2018 Miro Hrončok - 2.56.1-5 +- Rebuilt for Python 3.7 + +* Thu Jun 14 2018 Debarshi Ray - 2.56.1-4 +- Backport patch to fix possible invalid pointer in dbus callback in the FD.o + notification backend (RH #1584916) + +* Sun May 27 2018 Kalev Lember - 2.56.1-3 +- Fix multilib -devel installs (#1581067) + +* Sun May 13 2018 Fabio Valentini - 2.56.1-2 +- Include upstream patch to fix gdbus-codegen with meson 0.46. + +* Sun Apr 08 2018 Kalev Lember - 2.56.1-1 +- Update to 2.56.1 + +* Mon Mar 12 2018 Kalev Lember - 2.56.0-1 +- Update to 2.56.0 + +* Wed Feb 07 2018 Igor Gnatenko - 2.55.2-3 +- Undo disabling mangling + +* Wed Feb 07 2018 Kalev Lember - 2.55.2-2 +- Disable brp-mangle-shebangs shebangs + +* Wed Feb 07 2018 Kalev Lember - 2.55.2-1 +- Update to 2.55.2 +- Drop ldconfig scriptlets + +* Wed Jan 31 2018 Igor Gnatenko - 2.55.1-3 +- Switch to %%ldconfig_scriptlets + +* Thu Jan 18 2018 Kalev Lember - 2.55.1-2 +- gmain: Partial revert of recent wakeup changes + +* Mon Jan 08 2018 Kalev Lember - 2.55.1-1 +- Update to 2.55.1 +- Drop upstreamed systemtap multilib fix + +* Tue Dec 19 2017 Kalev Lember - 2.55.0-1 +- Update to 2.55.0 + +* Wed Nov 01 2017 Kalev Lember - 2.54.2-1 +- Update to 2.54.2 + +* Fri Oct 06 2017 Kalev Lember - 2.54.1-1 +- Update to 2.54.1 + +* Mon Sep 11 2017 Kalev Lember - 2.54.0-1 +- Update to 2.54.0 + +* Tue Sep 05 2017 Kalev Lember - 2.53.7-1 +- Update to 2.53.7 + +* Sat Aug 19 2017 Kalev Lember - 2.53.6-1 +- Update to 2.53.6 + +* Mon Aug 07 2017 Igor Gnatenko - 2.53.5-1 +- Update to 2.53.5 + +* Tue Aug 01 2017 Kalev Lember - 2.53.4-4 +- Backport glib-mkenums flags annotation parsing fixes + +* Wed Jul 26 2017 Fedora Release Engineering - 2.53.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 21 2017 Kalev Lember - 2.53.4-2 +- Revert a GKeyFile introspection ABI change + +* Tue Jul 18 2017 Kalev Lember - 2.53.4-1 +- Update to 2.53.4 + +* Thu Jun 22 2017 Kalev Lember - 2.53.3-1 +- Update to 2.53.3 + +* Thu Jun 8 2017 Owen Taylor - 2.53.2-2 +- Make triggers also compile schemas in /app/share/glib-2.0/schemas + +* Wed May 24 2017 Florian Müllner - 2.53.2-1 +- Update to 2.53.2 + +* Mon May 15 2017 Kalev Lember - 2.52.2-2 +- Backport a gmain GWakeup patch to fix timedatex high CPU usage (#1450628) + +* Tue May 09 2017 Kalev Lember - 2.52.2-1 +- Update to 2.52.2 + +* Tue Apr 11 2017 Colin Walters - 2.52.1-3 +- Backport patches for gmain wakeup for qemu + See: https://bugzilla.gnome.org/show_bug.cgi?id=761102 + +* Tue Apr 11 2017 Colin Walters - 2.52.1-2 +- Explictly remove PCRE sources +- Related: https://bugzilla.redhat.com/show_bug.cgi?id=1324770 + +* Tue Apr 11 2017 Kalev Lember - 2.52.1-1 +- Update to 2.52.1 + +* Mon Mar 20 2017 Kalev Lember - 2.52.0-1 +- Update to 2.52.0 + +* Thu Mar 16 2017 Kalev Lember - 2.51.5-1 +- Update to 2.51.5 + +* Thu Mar 02 2017 Kalev Lember - 2.51.4-2 +- Remove the dependency on dbus-launch again (#927212) + +* Wed Mar 01 2017 David King - 2.51.4-1 +- Update to 2.51.4 +- Add a Requires on dbus-launch (#927212) +- Use pkgconfig for BuildRequires + +* Tue Feb 14 2017 Richard Hughes - 2.51.2-1 +- Update to 2.51.2 + +* Mon Feb 13 2017 Richard Hughes - 2.51.1-1 +- Update to 2.51.1 + +* Fri Feb 10 2017 Fedora Release Engineering - 2.51.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Dec 19 2016 Miro Hrončok - 2.51.0-2 +- Rebuild for Python 3.6 + +* Sun Oct 30 2016 Kalev Lember - 2.51.0-1 +- Update to 2.51.0 + +* Wed Oct 12 2016 Kalev Lember - 2.50.1-1 +- Update to 2.50.1 + +* Mon Sep 19 2016 Kalev Lember - 2.50.0-1 +- Update to 2.50.0 + +* Tue Sep 13 2016 Kalev Lember - 2.49.7-1 +- Update to 2.49.7 +- Don't set group tags + +* Sun Aug 28 2016 Kalev Lember - 2.49.6-1 +- Update to 2.49.6 + +* Thu Aug 18 2016 Kalev Lember - 2.49.5-1 +- Update to 2.49.5 +- Own /usr/share/gdb and /usr/share/systemtap directories + +* Tue Aug 16 2016 Miro Hrončok - 2.49.4-3 +- Use Python 3 for the RPM Python byte compilation + +* Wed Jul 27 2016 Ville Skyttä - 2.49.4-2 +- Switch to Python 3 (#1286284) + +* Thu Jul 21 2016 Kalev Lember - 2.49.4-1 +- Update to 2.49.4 + +* Sun Jul 17 2016 Kalev Lember - 2.49.3-1 +- Update to 2.49.3 + +* Wed Jun 22 2016 Richard Hughes - 2.49.2-1 +- Update to 2.49.2 + +* Wed Jun 01 2016 Yaakov Selkowitz - 2.49.1-2 +- Soften shared-mime-info dependency (#1266118) + +* Fri May 27 2016 Florian Müllner - 2.49.1-1 +- Update to 2.49.1 + +* Tue May 10 2016 Kalev Lember - 2.48.1-1 +- Update to 2.48.1 + +* Wed Apr 06 2016 Colin Walters - 2.48.0-2 +- Explicitly require system pcre, though we happened to default to this now + anyways due to something else pulling PCRE into the buildroot + Closes rhbz#1287266 + +* Tue Mar 22 2016 Kalev Lember - 2.48.0-1 +- Update to 2.48.0 + +* Thu Mar 17 2016 Richard Hughes - 2.47.92-1 +- Update to 2.47.92 + +* Wed Feb 24 2016 Colin Walters - 2.47.6.19.gad2092b-2 +- git snapshot to work around https://bugzilla.gnome.org/show_bug.cgi?id=762637 +- Add --with-python=/usr/bin/python explicitly to hopefully fix a weird + issue I am seeing where librepo fails to build in epel7 with this due to + us requiring /bin/python. + +* Wed Feb 17 2016 Richard Hughes - 2.47.6-1 +- Update to 2.47.6 + +* Wed Feb 03 2016 Fedora Release Engineering - 2.47.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Jan 19 2016 David King - 2.47.5-1 +- Update to 2.47.5 + +* Wed Dec 16 2015 Kalev Lember - 2.47.4-1 +- Update to 2.47.4 + +* Wed Nov 25 2015 Kalev Lember - 2.47.3-1 +- Update to 2.47.3 + +* Wed Nov 25 2015 Kalev Lember - 2.47.2-1 +- Update to 2.47.2 + +* Mon Nov 09 2015 Kevin Fenzi - 2.47.1-2 +- Add full path redirect output to null and || : to triggers. + +* Wed Oct 28 2015 Kalev Lember - 2.47.1-1 +- Update to 2.47.1 + +* Mon Oct 19 2015 Kalev Lember - 2.46.1-2 +- Backport an upstream fix for app launching under wayland (#1273146) + +* Wed Oct 14 2015 Kalev Lember - 2.46.1-1 +- Update to 2.46.1 + +* Mon Sep 21 2015 Kalev Lember - 2.46.0-1 +- Update to 2.46.0 + +* Mon Sep 14 2015 Kalev Lember - 2.45.8-1 +- Update to 2.45.8 + +* Tue Sep 01 2015 Kalev Lember - 2.45.7-1 +- Update to 2.45.7 + +* Wed Aug 19 2015 Kalev Lember - 2.45.6-1 +- Update to 2.45.6 + +* Wed Aug 19 2015 Kalev Lember - 2.45.5-1 +- Update to 2.45.5 + +* Fri Aug 14 2015 Matthias Clasen - 2.45.4-2 +- Add file triggers for gio modules and gsettings schemas + +* Tue Jul 21 2015 David King - 2.45.4-1 +- Update to 2.45.4 + +* Wed Jun 24 2015 Kalev Lember - 2.45.3-2 +- Backport a patch to fix notification withdrawing in gnome-software + +* Wed Jun 24 2015 David King - 2.45.3-1 +- Update to 2.45.3 + +* Wed Jun 17 2015 Fedora Release Engineering - 2.45.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Tue May 26 2015 David King - 2.45.2-1 +- Update to 2.45.2 + +* Thu Apr 30 2015 Kalev Lember - 2.45.1-1 +- Update to 2.45.1 + +* Mon Mar 23 2015 Kalev Lember - 2.44.0-1 +- Update to 2.44.0 + +* Tue Mar 17 2015 Kalev Lember - 2.43.92-1 +- Update to 2.43.92 + +* Mon Mar 02 2015 Kalev Lember - 2.43.91-1 +- Update to 2.43.91 + +* Sat Feb 21 2015 Till Maas - 2.43.90-2 +- Rebuilt for Fedora 23 Change + https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code + +* Wed Feb 18 2015 David King - 2.43.90-1 +- Update to 2.43.90 +- Update man pages glob in files section + +* Tue Feb 10 2015 Matthias Clasen - 2.43.4-1 +- Update to 2.43.4 + +* Tue Jan 20 2015 David King - 2.43.3-1 +- Update to 2.43.3 + +* Wed Dec 17 2014 Kalev Lember - 2.43.2-1 +- Update to 2.43.2 + +* Tue Nov 25 2014 Kalev Lember - 2.43.1-1 +- Update to 2.43.1 + +* Thu Oct 30 2014 Florian Müllner - 2.43.0-1 +- Update to 2.43.0 + +* Mon Sep 22 2014 Kalev Lember - 2.42.0-1 +- Update to 2.42.0 + +* Tue Sep 16 2014 Kalev Lember - 2.41.5-1 +- Update to 2.41.5 + +* Thu Sep 4 2014 Matthias Clasen 2.41.4-3 +- Don't remove rpath from gdbus-peer test - it doesn't work without it + +* Thu Sep 04 2014 Bastien Nocera 2.41.4-2 +- Fix banshee getting selected as the default movie player + +* Tue Sep 02 2014 Kalev Lember - 2.41.4-1 +- Update to 2.41.4 + +* Sat Aug 16 2014 Kalev Lember - 2.41.3-1 +- Update to 2.41.3 + +* Sat Aug 16 2014 Fedora Release Engineering - 2.41.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Jul 23 2014 Stef Walter - 2.41.2-2 +- Fix regression with GDBus array encoding rhbz#1122128 + +* Mon Jul 14 2014 Kalev Lember - 2.41.2-1 +- Update to 2.41.2 + +* Sat Jul 12 2014 Tom Callaway - 2.41.1-2 +- fix license handling + +* Tue Jun 24 2014 Richard Hughes - 2.41.1-1 +- Update to 2.41.1 + +* Sat Jun 07 2014 Fedora Release Engineering - 2.41.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue May 27 2014 Kalev Lember - 2.41.0-1 +- Update to 2.41.0 + +* Mon Mar 24 2014 Richard Hughes - 2.40.0-1 +- Update to 2.40.0 + +* Tue Mar 18 2014 Richard Hughes - 2.39.92-1 +- Update to 2.39.92 + +* Tue Mar 04 2014 Richard Hughes - 2.39.91-1 +- Update to 2.39.91 + +* Tue Feb 18 2014 Richard Hughes - 2.39.90-1 +- Update to 2.39.90 + +* Tue Feb 04 2014 Richard Hughes - 2.39.4-1 +- Update to 2.39.4 + +* Tue Jan 14 2014 Richard Hughes - 2.39.3-1 +- Update to 2.39.3 + +* Sun Dec 22 2013 Richard W.M. Jones - 2.39.2-2 +- Re-add static subpackage so that we can build static qemu as + an AArch64 binfmt. + +* Tue Dec 17 2013 Richard Hughes - 2.39.2-1 +- Update to 2.39.2 + +* Mon Dec 09 2013 Richard Hughes - 2.39.1-2 +- Backport a patch from master to stop gnome-settings-daemon crashing. + +* Thu Nov 14 2013 Richard Hughes - 2.39.1-1 +- Update to 2.39.1 + +* Mon Oct 28 2013 Richard Hughes - 2.39.0-1 +- Update to 2.39.0 + +* Tue Sep 24 2013 Kalev Lember - 2.38.0-1 +- Update to 2.38.0 + +* Tue Sep 17 2013 Kalev Lember - 2.37.93-1 +- Update to 2.37.93 + +* Mon Sep 02 2013 Kalev Lember - 2.37.7-1 +- Update to 2.37.7 + +* Wed Aug 21 2013 Debarshi Ray - 2.37.6-1 +- Update to 2.37.6 + +* Sat Aug 03 2013 Petr Pisar - 2.37.5-2 +- Perl 5.18 rebuild + +* Thu Aug 1 2013 Debarshi Ray - 2.37.5-1 +- Update to 2.37.5 + +* Wed Jul 17 2013 Petr Pisar - 2.37.4-2 +- Perl 5.18 rebuild + +* Tue Jul 9 2013 Matthias Clasen - 2.37.4-1 +- Update to 2.37.4 + +* Thu Jun 20 2013 Debarshi Ray - 2.37.2-1 +- Update to 2.37.2 + +* Tue May 28 2013 Matthias Clasen - 2.37.1-1 +- Update to 2.37.1 +- Add a tests subpackage + +* Sat May 04 2013 Kalev Lember - 2.37.0-1 +- Update to 2.37.0 + +* Sat Apr 27 2013 Thorsten Leemhuis - 2.36.1-2 +- Fix pidgin freezes by applying patch from master (#956872) + +* Mon Apr 15 2013 Kalev Lember - 2.36.1-1 +- Update to 2.36.1 + +* Mon Mar 25 2013 Kalev Lember - 2.36.0-1 +- Update to 2.36.0 + +* Tue Mar 19 2013 Matthias Clasen - 2.35.9-1 +- Update to 2.35.9 + +* Thu Feb 21 2013 Kalev Lember - 2.35.8-1 +- Update to 2.35.8 + +* Tue Feb 05 2013 Kalev Lember - 2.35.7-1 +- Update to 2.35.7 + +* Tue Jan 15 2013 Matthias Clasen - 2.35.4-1 +- Update to 2.35.4 + +* Thu Dec 20 2012 Kalev Lember - 2.35.3-1 +- Update to 2.35.3 + +* Sat Nov 24 2012 Kalev Lember - 2.35.2-1 +- Update to 2.35.2 + +* Thu Nov 08 2012 Kalev Lember - 2.35.1-1 +- Update to 2.35.1 +- Drop upstreamed codegen-in-datadir.patch