[gnome-shell/benzea/systemd-3.36-units: 51/52] windowManager: Wait for X11 services using systemd
- From: Benjamin Berg <bberg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/benzea/systemd-3.36-units: 51/52] windowManager: Wait for X11 services using systemd
- Date: Thu, 30 Jul 2020 08:38:19 +0000 (UTC)
commit f9c063af966a12042f0690c6b2ba42fe6af411db
Author: Benjamin Berg <bberg redhat com>
Date: Fri Dec 13 19:07:16 2019 +0100
windowManager: Wait for X11 services using systemd
To do this, we now wait for the start/stop job to complete. We also have
two targets in gnome-session to ensure that everything is working as
expected.
In order to start the services, we simply request the
gnome-session-x11-services-ready.target unit, and wait for it to become
available. To stop, we use the gnome-session-x11-services.target unit
which should stop all services in a way that is entirely race free.
This requires both gnome-session and gnome-settings-daemon changes to
work (which are in the corresponding merge requests).
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/895
js/ui/windowManager.js | 78 +++++++++-------
src/shell-util.c | 238 ++++++++++++++++++++++++++++++++++++++++++-------
src/shell-util.h | 21 +++--
3 files changed, 264 insertions(+), 73 deletions(-)
---
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 2ff1ffdcbb..ab86a2e6a3 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -42,6 +42,11 @@ const GsdWacomProxy = Gio.DBusProxy.makeProxyWrapper(GsdWacomIface);
const WINDOW_DIMMER_EFFECT_NAME = "gnome-shell-window-dimmer";
+Gio._promisify(Shell,
+ 'util_start_systemd_unit', 'util_start_systemd_unit_finish');
+Gio._promisify(Shell,
+ 'util_stop_systemd_unit', 'util_stop_systemd_unit_finish');
+
var DisplayChangeDialog = GObject.registerClass(
class DisplayChangeDialog extends ModalDialog.ModalDialog {
_init(wm) {
@@ -901,46 +906,23 @@ var WindowManager = class {
global.display.connect('init-xserver', (display, task) => {
IBusManager.getIBusManager().restartDaemon(['--xim']);
- try {
- if (!Shell.util_start_systemd_unit('gsd-xsettings.target', 'fail'))
- log('Not starting gsd-xsettings; waiting for gnome-session to do so');
-
- /* Leave this watchdog timeout so don't block indefinitely here */
- let timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
- Gio.DBus.session.unwatch_name(watchId);
- log('Warning: Failed to start gsd-xsettings');
- task.return_boolean(true);
- timeoutId = 0;
- return GLib.SOURCE_REMOVE;
- });
+ /* Timeout waiting for start job completion after 5 seconds */
+ let cancellable = new Gio.Cancellable();
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
+ cancellable.cancel();
+ return GLib.SOURCE_REMOVE;
+ });
- /* When gsd-xsettings daemon is started, we are good to resume */
- let watchId = Gio.DBus.session.watch_name(
- 'org.gnome.SettingsDaemon.XSettings',
- Gio.BusNameWatcherFlags.NONE,
- () => {
- Gio.DBus.session.unwatch_name(watchId);
- if (timeoutId > 0) {
- task.return_boolean(true);
- GLib.source_remove(timeoutId);
- }
- },
- null);
- } catch (e) {
- log('Error starting gsd-xsettings: %s'.format(e.message));
- task.return_boolean(true);
- }
+ this._startX11Services(task, cancellable);
return true;
});
global.display.connect('x11-display-closing', () => {
if (!Meta.is_wayland_compositor())
return;
- try {
- Shell.util_stop_systemd_unit('gsd-xsettings.target', 'fail');
- } catch (e) {
- log('Error stopping gsd-xsettings: %s'.format(e.message));
- }
+
+ this._stopX11Services(null);
+
IBusManager.getIBusManager().restartDaemon();
});
@@ -1008,6 +990,36 @@ var WindowManager = class {
global.stage.add_action(topDragAction);
}
+ async _startX11Services(task, cancellable) {
+ try {
+ await Shell.util_start_systemd_unit(
+ 'gnome-session-x11-services-ready.target', 'fail', cancellable);
+ } catch (e) {
+ // Ignore NOT_SUPPORTED error, which indicates we are not systemd
+ // managed and gnome-session will have taken care of everything
+ // already.
+ // Note that we do log cancellation from here.
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))
+ log('Error starting X11 services: %s'.format(e.message));
+ } finally {
+ task.return_boolean(true);
+ }
+ }
+
+ async _stopX11Services(cancellable) {
+ try {
+ await Shell.util_stop_systemd_unit(
+ 'gnome-session-x11-services.target', 'fail', cancellable);
+ } catch (e) {
+ // Ignore NOT_SUPPORTED error, which indicates we are not systemd
+ // managed and gnome-session will have taken care of everything
+ // already.
+ // Note that we do log cancellation from here.
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))
+ log('Error stopping X11 services: %s'.format(e.message));
+ }
+ }
+
_showPadOsd(display, device, settings, imagePath, editionMode, monitorIndex) {
this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
this._currentPadOsd.connect('closed', () => (this._currentPadOsd = null));
diff --git a/src/shell-util.c b/src/shell-util.c
index 4a6e1d706d..37b0cfaf23 100644
--- a/src/shell-util.c
+++ b/src/shell-util.c
@@ -570,6 +570,57 @@ shell_util_get_uid (void)
return getuid ();
}
+typedef struct {
+ GDBusConnection *connection;
+ gchar *command;
+
+ GCancellable *cancellable;
+ gulong cancel_id;
+
+ guint job_watch;
+ gchar *job;
+} SystemdCall;
+
+static void
+shell_util_systemd_call_data_free (SystemdCall *data)
+{
+ if (data->job_watch)
+ {
+ g_dbus_connection_signal_unsubscribe (data->connection, data->job_watch);
+ data->job_watch = 0;
+ }
+
+ if (data->cancellable)
+ {
+ g_cancellable_disconnect (data->cancellable, data->cancel_id);
+ g_clear_object (&data->cancellable);
+ data->cancel_id = 0;
+ }
+
+ g_clear_object (&data->connection);
+ g_clear_pointer (&data->job, g_free);
+ g_clear_pointer (&data->command, g_free);
+ g_free (data);
+}
+
+static void
+shell_util_systemd_call_cancelled_cb (GCancellable *cancellable,
+ GTask *task)
+{
+ SystemdCall *data = g_task_get_task_data (task);
+
+ /* Task has returned, but data is not yet free'ed, ignore signal. */
+ if (g_task_get_completed (task))
+ return;
+
+ /* We are still in the DBus call; it will return the error. */
+ if (data->job == NULL)
+ return;
+
+ g_task_return_error_if_cancelled (task);
+ g_object_unref (task);
+}
+
static void
on_systemd_call_cb (GObject *source,
GAsyncResult *res,
@@ -577,46 +628,143 @@ on_systemd_call_cb (GObject *source,
{
g_autoptr (GVariant) reply = NULL;
g_autoptr (GError) error = NULL;
- const gchar *command = user_data;
+ GTask *task = G_TASK (user_data);
+ SystemdCall *data;
reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
res, &error);
- if (error)
- g_warning ("Could not issue '%s' systemd call", command);
+
+ data = g_task_get_task_data (task);
+
+ if (error) {
+ g_warning ("Could not issue '%s' systemd call", data->command);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+
+ return;
+ }
+
+ g_assert (data->job == NULL);
+ g_variant_get (reply, "(o)", &data->job);
+
+ /* And we wait for the JobRemoved notification. */
}
-static gboolean
-shell_util_systemd_call (const char *command,
- const char *unit,
- const char *mode,
- GError **error)
+static void
+on_systemd_job_removed_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
{
+ GTask *task = G_TASK (user_data);
+ SystemdCall *data;
+ guint32 id;
+ const char *path, *unit, *result;
+
+ /* Task has returned, but data is not yet free'ed, ignore signal. */
+ if (g_task_get_completed (task))
+ return;
+
+ data = g_task_get_task_data (task);
+
+ /* No job information yet, ignore. */
+ if (data->job == NULL)
+ return;
+
+ g_variant_get (parameters, "(u&o&s&s)", &id, &path, &unit, &result);
+
+ /* Is it the job we are waiting for? */
+ if (g_strcmp0 (path, data->job) != 0)
+ return;
+
+ /* Task has completed; return the result of the job */
+ if (g_strcmp0 (result, "done") == 0)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Systemd job completed with status \"%s\"",
+ result);
+
+ g_object_unref (task);
+}
+
+static void
+shell_util_systemd_call (const char *command,
+ const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = g_task_new (NULL, cancellable, callback, user_data);
+
#ifdef HAVE_SYSTEMD
g_autoptr (GDBusConnection) connection = NULL;
+ GError *error = NULL;
+ SystemdCall *data;
g_autofree char *self_unit = NULL;
int res;
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (connection == NULL) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* We look up the systemd unit that our own process is running in here.
+ * This way we determine whether the session is managed using systemd.
+ */
res = sd_pid_get_user_unit (getpid (), &self_unit);
if (res == -ENODATA)
{
- g_debug ("Not systemd-managed, not doing '%s' on '%s'", mode, unit);
- return FALSE;
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Not systemd managed");
+ return;
}
else if (res < 0)
{
- g_set_error (error,
- G_IO_ERROR,
- g_io_error_from_errno (-res),
- "Error trying to start systemd unit '%s': %s",
- unit, g_strerror (-res));
- return FALSE;
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ g_io_error_from_errno (-res),
+ "Error fetching own systemd unit: %s",
+ g_strerror (-res));
+ return;
}
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
-
- if (connection == NULL)
- return FALSE;
+ data = g_new0 (SystemdCall, 1);
+ data->command = g_strdup (command);
+ data->connection = g_object_ref (connection);
+ data->job_watch = g_dbus_connection_signal_subscribe (connection,
+ "org.freedesktop.systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ "/org/freedesktop/systemd1",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_systemd_job_removed_cb,
+ task,
+ NULL);
+ g_task_set_task_data (task,
+ data,
+ (GDestroyNotify) shell_util_systemd_call_data_free);
+
+ if (cancellable)
+ {
+ data->cancellable = g_object_ref (cancellable);
+ data->cancel_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (shell_util_systemd_call_cancelled_cb),
+ task,
+ NULL);
+ }
g_dbus_connection_call (connection,
"org.freedesktop.systemd1",
@@ -625,31 +773,53 @@ shell_util_systemd_call (const char *command,
command,
g_variant_new ("(ss)",
unit, mode),
- NULL,
+ G_VARIANT_TYPE ("(o)"),
G_DBUS_CALL_FLAGS_NONE,
- -1, NULL,
+ -1, cancellable,
on_systemd_call_cb,
- (gpointer) command);
- return TRUE;
-#endif /* HAVE_SYSTEMD */
+ g_steal_pointer (&task));
+#else /* HAVE_SYSTEMD */
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "systemd not supported by gnome-shell");
+#endif /* !HAVE_SYSTEMD */
+}
- return FALSE;
+void
+shell_util_start_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ shell_util_systemd_call ("StartUnit", unit, mode,
+ cancellable, callback, user_data);
}
gboolean
-shell_util_start_systemd_unit (const char *unit,
- const char *mode,
- GError **error)
+shell_util_start_systemd_unit_finish (GAsyncResult *res,
+ GError **error)
{
- return shell_util_systemd_call ("StartUnit", unit, mode, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+shell_util_stop_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ shell_util_systemd_call ("StopUnit", unit, mode,
+ cancellable, callback, user_data);
}
gboolean
-shell_util_stop_systemd_unit (const char *unit,
- const char *mode,
- GError **error)
+shell_util_stop_systemd_unit_finish (GAsyncResult *res,
+ GError **error)
{
- return shell_util_systemd_call ("StopUnit", unit, mode, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
diff --git a/src/shell-util.h b/src/shell-util.h
index 29b9a64789..d0de033f61 100644
--- a/src/shell-util.h
+++ b/src/shell-util.h
@@ -59,12 +59,21 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures
void shell_util_check_cloexec_fds (void);
-gboolean shell_util_start_systemd_unit (const char *unit,
- const char *mode,
- GError **error);
-gboolean shell_util_stop_systemd_unit (const char *unit,
- const char *mode,
- GError **error);
+void shell_util_start_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_start_systemd_unit_finish (GAsyncResult *res,
+ GError **error);
+
+void shell_util_stop_systemd_unit (const char *unit,
+ const char *mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean shell_util_stop_systemd_unit_finish (GAsyncResult *res,
+ GError **error);
void shell_util_sd_notify (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]