From f7fea0b26f2a18f920564b6df017f915b4c5712f Mon Sep 17 00:00:00 2001
From: Eric Koegel <eric.koegel@gmail.com>
Date: Mon, 17 Nov 2014 12:43:39 +0300
Subject: [PATCH 06/12] Keep track of the idle callback

This way we can prevent the user applications from attempting to
perform multiple system events at the same time. The additional
attempts will fail.
---
 src/ck-manager.c | 68 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 52 insertions(+), 16 deletions(-)

diff --git a/src/ck-manager.c b/src/ck-manager.c
index 0110d89..e1bf022 100644
--- a/src/ck-manager.c
+++ b/src/ck-manager.c
@@ -86,6 +86,9 @@ struct CkManagerPrivate
         /* How long to delay after emitting the PREPARE_FOR_SHUTDOWN or
          * PREPARE_FOR_SLEEP signal */
         guint            system_action_idle_delay;
+        /* The idle callback id so we can detect multiple attempts to
+         * perform a system action at the same time */
+        guint            system_action_idle_id;
 
         CkInhibitManager *inhibit_manager;
 };
@@ -1239,7 +1242,7 @@ system_action_idle_cb(SystemActionData *data)
 {
         g_return_val_if_fail (data != NULL, FALSE);
 
-        /* Perform the action */
+        /* Perform the action, it will handle the dbus_g_method_return */
         do_system_action (data->manager,
                           data->context,
                           data->command,
@@ -1250,6 +1253,9 @@ system_action_idle_cb(SystemActionData *data)
          * or the operation failed. Either way we can signal to the apps */
         g_signal_emit (data->manager, signals [data->signal], 0, FALSE);
 
+        /* reset this since we'll return FALSE here and kill the cb */
+        data->manager->priv->system_action_idle_id = 0;
+
         g_free (data);
 
         return FALSE;
@@ -1261,6 +1267,13 @@ do_restart (CkManager             *manager,
 {
         SystemActionData *data;
 
+        /* Don't allow multiple system actions at the same time */
+        if (manager->priv->system_action_idle_id != 0) {
+                g_error ("attempting to perform a system action while one is in progress");
+                dbus_g_method_return (context, FALSE);
+                return;
+        }
+
         /* Emit the signal */
         g_signal_emit (manager, signals [PREPARE_FOR_SHUTDOWN], 0, TRUE);
 
@@ -1280,9 +1293,9 @@ do_restart (CkManager             *manager,
         data->signal = PREPARE_FOR_SHUTDOWN;
 
         /* Sleep so user applications have time to respond */
-        g_timeout_add (data->manager->priv->system_action_idle_delay,
-                       (GSourceFunc)system_action_idle_cb,
-                       data);
+        manager->priv->system_action_idle_id = g_timeout_add (data->manager->priv->system_action_idle_delay,
+                                                              (GSourceFunc)system_action_idle_cb,
+                                                              data);
 }
 
 /*
@@ -1347,6 +1360,13 @@ do_stop (CkManager             *manager,
 {
         SystemActionData *data;
 
+        /* Don't allow multiple system actions at the same time */
+        if (manager->priv->system_action_idle_id != 0) {
+                g_error ("attempting to perform a system action while one is in progress");
+                dbus_g_method_return (context, FALSE);
+                return;
+        }
+
         /* Emit the signal */
         g_signal_emit (manager, signals [PREPARE_FOR_SHUTDOWN], 0, TRUE);
 
@@ -1355,6 +1375,7 @@ do_stop (CkManager             *manager,
         if (data == NULL) {
                 g_critical ("failed to allocate memory to perform shutdown\n");
                 g_signal_emit (manager, signals [PREPARE_FOR_SHUTDOWN], 0, FALSE);
+                dbus_g_method_return (context, FALSE);
                 return;
         }
 
@@ -1366,9 +1387,9 @@ do_stop (CkManager             *manager,
         data->signal = PREPARE_FOR_SHUTDOWN;
 
         /* Sleep so user applications have time to respond */
-        g_timeout_add (data->manager->priv->system_action_idle_delay,
-                       (GSourceFunc)system_action_idle_cb,
-                       data);
+        manager->priv->system_action_idle_id = g_timeout_add (data->manager->priv->system_action_idle_delay,
+                                                              (GSourceFunc)system_action_idle_cb,
+                                                              data);
 }
 
 gboolean
@@ -1563,6 +1584,13 @@ do_suspend (CkManager             *manager,
 {
         SystemActionData *data;
 
+        /* Don't allow multiple system actions at the same time */
+        if (manager->priv->system_action_idle_id != 0) {
+                g_error ("attempting to perform a system action while one is in progress");
+                dbus_g_method_return (context, FALSE);
+                return;
+        }
+
         /* Emit the signal */
         g_signal_emit (manager, signals [PREPARE_FOR_SLEEP], 0, TRUE);
 
@@ -1571,6 +1599,7 @@ do_suspend (CkManager             *manager,
         if (data == NULL) {
                 g_critical ("failed to allocate memory to perform suspend\n");
                 g_signal_emit (manager, signals [PREPARE_FOR_SLEEP], 0, FALSE);
+                dbus_g_method_return (context, FALSE);
                 return;
         }
 
@@ -1582,9 +1611,9 @@ do_suspend (CkManager             *manager,
         data->signal = PREPARE_FOR_SLEEP;
 
         /* Sleep so user applications have time to respond */
-        g_timeout_add (data->manager->priv->system_action_idle_delay,
-                       (GSourceFunc)system_action_idle_cb,
-                       data);
+        manager->priv->system_action_idle_id = g_timeout_add (data->manager->priv->system_action_idle_delay,
+                                                              (GSourceFunc)system_action_idle_cb,
+                                                              data);
 }
 
 /*
@@ -1676,6 +1705,7 @@ do_hibernate (CkManager             *manager,
         if (data == NULL) {
                 g_critical ("failed to allocate memory to perform suspend\n");
                 g_signal_emit (manager, signals [PREPARE_FOR_SLEEP], 0, FALSE);
+                dbus_g_method_return (context, FALSE);
                 return;
         }
 
@@ -1687,9 +1717,9 @@ do_hibernate (CkManager             *manager,
         data->signal = PREPARE_FOR_SLEEP;
 
         /* Sleep so user applications have time to respond */
-        g_timeout_add (data->manager->priv->system_action_idle_delay,
-                       (GSourceFunc)system_action_idle_cb,
-                       data);
+        manager->priv->system_action_idle_id = g_timeout_add (data->manager->priv->system_action_idle_delay,
+                                                              (GSourceFunc)system_action_idle_cb,
+                                                              data);
 }
 
 /*
@@ -1781,6 +1811,7 @@ do_hybrid_sleep (CkManager             *manager,
         if (data == NULL) {
                 g_critical ("failed to allocate memory to perform suspend\n");
                 g_signal_emit (manager, signals [PREPARE_FOR_SLEEP], 0, FALSE);
+                dbus_g_method_return (context, FALSE);
                 return;
         }
 
@@ -1792,9 +1823,9 @@ do_hybrid_sleep (CkManager             *manager,
         data->signal = PREPARE_FOR_SLEEP;
 
         /* Sleep so user applications have time to respond */
-        g_timeout_add (data->manager->priv->system_action_idle_delay,
-                       (GSourceFunc)system_action_idle_cb,
-                       data);
+        manager->priv->system_action_idle_id = g_timeout_add (data->manager->priv->system_action_idle_delay,
+                                                              (GSourceFunc)system_action_idle_cb,
+                                                              data);
 }
 
 /*
@@ -3409,6 +3440,7 @@ ck_manager_init (CkManager *manager)
         manager->priv->inhibit_manager = ck_inhibit_manager_get ();
 
         manager->priv->system_action_idle_delay = 4 * 1000;
+        manager->priv->system_action_idle_id = 0;
 
         create_seats (manager);
 }
@@ -3440,6 +3472,10 @@ ck_manager_finalize (GObject *object)
                 g_object_unref (manager->priv->inhibit_manager);
         }
 
+        if (manager->priv->system_action_idle_id != 0) {
+                g_source_remove (manager->priv->system_action_idle_id);
+        }
+
         G_OBJECT_CLASS (ck_manager_parent_class)->finalize (object);
 }
 
-- 
2.2.1

