[gnome-shell/benzea/systemd-start-waiting] util: Improve systemd start/stop API to permit waiting for job



commit b92afe6a635288b2ef4c1c803f0617b939be0296
Author: Benjamin Berg <bberg redhat com>
Date:   Fri Dec 13 19:07:16 2019 +0100

    util: Improve systemd start/stop API to permit waiting for job
    
    In some cases it may be interesting to wait for the start/stop job to
    complete. Improve the API to be asynchronous and add the appropriate
    watches for systemd. Only return the task once the start or stop job has
    been removed again on the systemd side.

 js/ui/windowManager.js |   4 +-
 src/shell-util.c       | 195 ++++++++++++++++++++++++++++++++++++++++++-------
 src/shell-util.h       |  23 ++++--
 3 files changed, 189 insertions(+), 33 deletions(-)
---
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 41544ee7bd..41779dee6d 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 f344ec50bb..fe000b8f2c 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 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 shell_util_systemd_call ("StopUnit", unit, mode, error);
+  return g_task_propagate_pointer (G_TASK (res), error);
 }
 
 void
diff --git a/src/shell-util.h b/src/shell-util.h
index acd031048f..0dca7d265d 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);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]