diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 41544ee7b..41779dee6 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -1044,10 +1044,10 @@ var WindowManager = class { global.display.connect('x11-display-opened', () => { IBusManager.getIBusManager().restartDaemon(['--xim']); - Shell.util_start_systemd_unit('gnome-session-x11-services.target', 'fail'); + Shell.util_start_systemd_unit('gnome-session-x11-services.target', 'fail', null, () => {}); }); global.display.connect('x11-display-closing', () => { - Shell.util_stop_systemd_unit('gnome-session-x11-services.target', 'fail'); + Shell.util_stop_systemd_unit('gnome-session-x11-services.target', 'fail', null, () => {}); IBusManager.getIBusManager().restartDaemon(); }); diff --git a/src/shell-util.c b/src/shell-util.c index f344ec50b..fe000b8f2 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -573,6 +573,53 @@ shell_util_check_cloexec_fds (void) g_info ("Open fd CLOEXEC check complete"); } +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); + + /* We are still in the DBus call; it will return the error. */ + if (data->job == NULL) + return; + + /* Return the cancellation error now. */ + g_assert (g_task_return_error_if_cancelled (task)); +} + static void on_systemd_call_cb (GObject *source, GAsyncResult *res, @@ -580,26 +627,103 @@ 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, 0, "(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; + + 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 */ + g_task_return_pointer (task, g_strdup (result), g_free); + 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 (GDBusConnection) connection = NULL; + g_autoptr (GTask) task = NULL; + GError *error = NULL; + SystemdCall *data; - connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + task = g_task_new (NULL, cancellable, callback, user_data); - if (connection == NULL) - return FALSE; + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (connection == NULL) { + g_task_return_error (task, error); + return; + } + + 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, + g_object_ref (task), + g_object_unref); + g_task_set_task_data (task, + data, + (GDestroyNotify) shell_util_systemd_call_data_free); + + if (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", @@ -608,28 +732,49 @@ 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; + g_steal_pointer (&task)); } -gboolean -shell_util_start_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) { - return shell_util_systemd_call ("StartUnit", unit, mode, error); + shell_util_systemd_call ("StartUnit", unit, mode, + cancellable, callback, user_data); } -gboolean -shell_util_stop_systemd_unit (const char *unit, - const char *mode, - GError **error) +gchar* +shell_util_start_systemd_unit_finish (GObject *obj, + GAsyncResult *res, + GError **error) { - return shell_util_systemd_call ("StopUnit", unit, mode, error); + return g_task_propagate_pointer (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); +} + +gchar* +shell_util_stop_systemd_unit_finish (GObject *obj, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); } void diff --git a/src/shell-util.h b/src/shell-util.h index acd031048..0dca7d265 100644 --- a/src/shell-util.h +++ b/src/shell-util.h @@ -57,12 +57,23 @@ 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); +gchar* shell_util_start_systemd_unit_finish (GObject *obj, + GAsyncResult *res, + GError **error); + +void shell_util_stop_systemd_unit (const char *unit, + const char *mode, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gchar* shell_util_stop_systemd_unit_finish (GObject *obj, + GAsyncResult *res, + GError **error); void shell_util_sd_notify (void);