From ea2cef426d6c58409c4130b52f267cba9c0b8f0f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 12 Oct 2009 22:34:58 -0400 Subject: [PATCH 18/21] Introduce QueryStop and CancelStop This adds the QueryStop method which can be called to give all sessions a chance to add just-in-time inhibitors. It also adds the CancelStop method, which can be used to tell all sessions that the shutdown has been canceled. --- data/ConsoleKit.conf | 6 + src/ck-manager.c | 231 +++++++++++++++++++++++++++- src/ck-manager.h | 6 +- src/org.freedesktop.ConsoleKit.Manager.xml | 62 ++++++++ 4 files changed, 298 insertions(+), 7 deletions(-) diff --git a/data/ConsoleKit.conf b/data/ConsoleKit.conf index f46fb5c..4995d2d 100644 --- a/data/ConsoleKit.conf +++ b/data/ConsoleKit.conf @@ -71,6 +71,12 @@ send_member="GetSystemIdleSinceHint"/> + + manager = manager; ready->context = context; @@ -824,7 +827,7 @@ get_polkit_permissions (CkManager *manager, g_debug ("get permissions for action %s", action); - sender = dbus_g_method_get_sender (ready->context); + sender = dbus_g_method_get_sender (context); subject = polkit_system_bus_name_new (sender); ready = g_new0 (AuthReady, 1); @@ -1069,8 +1072,8 @@ do_stop (AuthReady *ready, GError *new_error; new_error = g_error_new (CK_MANAGER_ERROR, - CK_MANAGER_ERROR_NOT_PRIVILEGED, - "Not Authorized: %s", new_error->message); + CK_MANAGER_ERROR_GENERAL, + "Failed to obtain authorization: %s", error->message); dbus_g_method_return_error (ready->context, new_error); g_error_free (new_error); cleanup_after_stop (ready->manager); @@ -1184,7 +1187,13 @@ permission_ready (AuthReady *ready, GError *error) { if (result < 0) { - dbus_g_method_return_error (ready->context, error); + GError *new_error; + + new_error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Failed to obtain authorization: %s", error->message); + dbus_g_method_return_error (ready->context, new_error); + g_error_free (new_error); } else { dbus_g_method_return (ready->context, result != 0); } @@ -1607,6 +1616,112 @@ generate_unique_inhibitor_cookie (CkManager *manager) } static void +remove_inhibitors_for_session (CkManager *manager, + const char *session_id) +{ + GHashTableIter iter; + CkInhibitor *inhibitor; + + g_hash_table_iter_init (&iter, manager->priv->inhibitors); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&inhibitor)) { + if (g_strcmp0 (session_id, ck_inhibitor_peek_session_id (inhibitor)) == 0) { + g_signal_emit (manager, signals[INHIBITOR_REMOVED], 0, ck_inhibitor_peek_id (inhibitor)); + g_hash_table_iter_remove (&iter); + } + } +} + +static void +permissions_obtained (AuthReady *ready, + gint can_proceed, + GError *error) +{ + guint inhibitors; + guint sessions; + + if (can_proceed < 0) { + GError *new_error; + + new_error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Failed to obtain authorization: %s", error->message); + dbus_g_method_return_error (ready->context, new_error); + g_error_free (new_error); + } else { + inhibitors = g_hash_table_size (ready->manager->priv->inhibitors); + sessions = g_hash_table_size (ready->manager->priv->sessions); + + g_debug ("QueryStop: complete, can_proceed=%d inhibitors=%d sessions=%d", can_proceed, inhibitors, sessions); + + dbus_g_method_return (ready->context, can_proceed, inhibitors, sessions); + } +} + +static void +query_stop_complete (CkManager *manager) +{ + const char *action; + +#ifdef HAVE_POLKIT + if (need_elevated_privileges (manager)) { + action = "org.freedesktop.consolekit.system.restart-multiple-users"; + } else { + action = "org.freedesktop.consolekit.system.restart"; + } + + g_debug ("QueryStop: checking privileges for %s", action); + + get_polkit_permissions (manager, manager->priv->query_context, action, permissions_obtained); +#else + permissions_obtained (manager, 2, NULL); +#endif +} + +static void +on_session_stop_response (CkSession *session, + gboolean ok, + const char *reason, + CkManager *manager) +{ + const char *id; + + id = ck_session_peek_id (session); + + manager->priv->query_sessions = g_slist_remove (manager->priv->query_sessions, session); + + if (manager->priv->query_context) { + /* we are in a QueryStop call, deal with inhibitors */ + if (ok) { + remove_inhibitors_for_session (manager, id); + } + else { + /* create a jit inhibitor */ + CkInhibitor *inhibitor; + guint cookie; + + cookie = generate_unique_inhibitor_cookie (manager); + inhibitor = ck_inhibitor_new_for_session (NULL, + id, + CK_INHIBITOR_FLAG_SHUTDOWN, + reason, + cookie); + g_hash_table_insert (manager->priv->inhibitors, GINT_TO_POINTER (cookie), inhibitor); + g_signal_emit (manager, signals[INHIBITOR_ADDED], 0, ck_inhibitor_peek_id (inhibitor)); + } + } + + if (manager->priv->query_sessions == NULL) { + + g_debug ("got all session responses"); + + if (manager->priv->query_context != NULL) { + /* we are in a QueryStop call */ + query_stop_complete (manager); + } + } +} + +static void open_session_for_leader (CkManager *manager, CkSessionLeader *leader, const GPtrArray *parameters, @@ -1640,6 +1755,9 @@ open_session_for_leader (CkManager *manager, g_strdup (ssid), g_object_ref (session)); + g_signal_connect (session, "stop-response", + G_CALLBACK (on_session_stop_response), manager); + /* Add to seat */ seat = find_seat_for_session (manager, session); if (seat == NULL) { @@ -2820,3 +2938,104 @@ ck_manager_get_inhibitors (CkManager *manager, return TRUE; } +static void +remove_all_session_inhibitors (CkManager *manager) +{ + GHashTableIter iter; + CkInhibitor *inhibitor; + + g_hash_table_iter_init (&iter, manager->priv->inhibitors); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&inhibitor)) { + if (ck_inhibitor_peek_session_id (inhibitor) != NULL) { + g_signal_emit (manager, signals[INHIBITOR_REMOVED], 0, ck_inhibitor_peek_id (inhibitor)); + g_hash_table_iter_remove (&iter); + } + } +} + +gboolean +ck_manager_query_stop (CkManager *manager, + DBusGMethodInvocation *context) +{ + GError *error; + GHashTableIter iter; + CkSession *session; + + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + if (manager->priv->query_context != NULL) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "QueryStop in progress"); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_debug ("QueryStop in progress"); + + return FALSE; + } + + if (manager->priv->stopping) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Stop in progress"); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_debug ("Stop in progress"); + + return FALSE; + } + + g_debug ("QueryStop begins"); + + manager->priv->query_context = context; + manager->priv->query_sessions = NULL; + + /* just in case, remove stale jit inhibitors; there shouldn't be any */ + remove_all_session_inhibitors (manager); + + g_hash_table_iter_init (&iter, manager->priv->sessions); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&session)) { + manager->priv->query_sessions = g_slist_prepend (manager->priv->query_sessions, session); + ck_session_query_stop (session); + } + + return FALSE; +} + +gboolean +ck_manager_cancel_stop (CkManager *manager, + DBusGMethodInvocation *context) +{ + GError *error; + + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + if (manager->priv->query_context != NULL) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "QueryStop in progress"); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_debug ("QueryStop in progress"); + + return FALSE; + } + + if (manager->priv->stopping) { + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Stop in progress"); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_debug ("Stop in progress"); + + return FALSE; + } + + g_debug ("CancelStop"); + + cleanup_after_stop (manager); + dbus_g_method_return (context); + + return TRUE; +} diff --git a/src/ck-manager.h b/src/ck-manager.h index 3d42bd0..3e18e16 100644 --- a/src/ck-manager.h +++ b/src/ck-manager.h @@ -89,9 +89,13 @@ gboolean ck_manager_restart (CkManager DBusGMethodInvocation *context); gboolean ck_manager_can_stop (CkManager *manager, - DBusGMethodInvocation *context); + DBusGMethodInvocation *context); gboolean ck_manager_can_restart (CkManager *manager, DBusGMethodInvocation *context); +gboolean ck_manager_query_stop (CkManager *manager, + DBusGMethodInvocation *context); +gboolean ck_manager_cancel_stop (CkManager *manager, + DBusGMethodInvocation *context); /* Inhibit api */ gboolean ck_manager_inhibit (CkManager *manager, diff --git a/src/org.freedesktop.ConsoleKit.Manager.xml b/src/org.freedesktop.ConsoleKit.Manager.xml index 51242ad..ebc0d00 100644 --- a/src/org.freedesktop.ConsoleKit.Manager.xml +++ b/src/org.freedesktop.ConsoleKit.Manager.xml @@ -354,6 +354,68 @@ + + + + + + Whether privileges are required to go restart or stop the machine. + 0 means 'not allowed', 1 means 'extra privileges required', and + 2 means 'allowed'. + + + + + + + The number of inhibitors + + + + + + + The number of sessions + + + + + + + This function should be called when a machine restart or stop is + about to happen, before calling Restart() + or Stop(). + It returns information about obstacles to the restart operation + (so-called inhibitors) + and about user sessions that would be disrupted. + + + To gather this information, the QueryStop + signal is emitted on all session objects. + + + The caller is responsible for following the QueryStop() call with + either a Restart() + or Stop() call + to proceed with the operation, or a CancelStop() + call to notify other sessions that 'the danger is over'. + + + + + + + + + + + Cancels a restart or stop operation that was initiated with a + QueryStop() call. + + + + + -- 1.6.5.rc2