diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 2ff1ffdcb..ab86a2e6a 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'); + /* 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; + }); - /* 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; - }); - - /* 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 4a6e1d706..37b0cfaf2 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); + 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 (connection == NULL) - return FALSE; + 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 29b9a6478..d0de033f6 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);