[gnome-session] Systemd: Handle polkit dialog at session end more gracefully
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-session] Systemd: Handle polkit dialog at session end more gracefully
- Date: Fri, 23 Nov 2012 18:20:46 +0000 (UTC)
commit 1c84d28345289636aea23acaa24b0bd7660bbe09
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Nov 22 21:24:22 2012 -0500
Systemd: Handle polkit dialog at session end more gracefully
When there are multiple sessions, we may get a polkit dialog after
the user confirms the power off dialog. Previously, we didn't handle
this situation very nicely, since we were already in the EndSession
phase when calling PowerOff, and there is no way back to the Running
phase from there, even if the user cancels the polkit dialog.
This commit rearranges things so that we call PowerOff before leaving
the QueryEndSession phase. To prevent the system from going down
right away, we take a delay inhibitor, and listen for the
PrepareForShutdown signal to know when we can safely transition
into EndSession. If the user cancels the polkit dialog, the PowerOff
call fails with an AccessDenied error, which we handle and return
to the Running phase. In order to tell gnome-shell that the session
is back to running, we call a new Close() method on the EndSessionDialog
interface.
All of this only works with systemd. The ConsoleKit implementation
works the same as before.
https://bugzilla.gnome.org/show_bug.cgi?id=688076
gnome-session/gsm-consolekit.c | 24 ++++++
gnome-session/gsm-manager.c | 67 ++++++++++++++++++
gnome-session/gsm-shell.c | 9 +++
gnome-session/gsm-shell.h | 1 +
gnome-session/gsm-system.c | 24 ++++++-
gnome-session/gsm-system.h | 11 +++
gnome-session/gsm-systemd.c | 151 +++++++++++++++++++++++++++++++++++++++-
7 files changed, 283 insertions(+), 4 deletions(-)
---
diff --git a/gnome-session/gsm-consolekit.c b/gnome-session/gsm-consolekit.c
index 26f0dd5..2ae20cf 100644
--- a/gnome-session/gsm-consolekit.c
+++ b/gnome-session/gsm-consolekit.c
@@ -58,6 +58,7 @@ struct _GsmConsolekitPrivate
UpClient *up_client;
gboolean is_active;
+ gboolean restarting;
};
enum {
@@ -960,6 +961,27 @@ gsm_consolekit_remove_inhibitor (GsmSystem *system,
}
static void
+gsm_consolekit_prepare_shutdown (GsmSystem *system,
+ gboolean restart)
+{
+ GsmConsolekit *consolekit = GSM_CONSOLEKIT (system);
+
+ consolekit->priv->restarting = restart;
+ g_signal_emit_by_name (system, "shutdown-prepared", TRUE);
+}
+
+static void
+gsm_consolekit_complete_shutdown (GsmSystem *system)
+{
+ GsmConsolekit *consolekit = GSM_CONSOLEKIT (system);
+
+ if (consolekit->priv->restarting)
+ gsm_consolekit_attempt_restart (system);
+ else
+ gsm_consolekit_attempt_stop (system);
+}
+
+static void
gsm_consolekit_system_init (GsmSystemInterface *iface)
{
iface->can_switch_user = gsm_consolekit_can_switch_user;
@@ -975,6 +997,8 @@ gsm_consolekit_system_init (GsmSystemInterface *iface)
iface->is_login_session = gsm_consolekit_is_login_session;
iface->add_inhibitor = gsm_consolekit_add_inhibitor;
iface->remove_inhibitor = gsm_consolekit_remove_inhibitor;
+ iface->prepare_shutdown = gsm_consolekit_prepare_shutdown;
+ iface->complete_shutdown = gsm_consolekit_complete_shutdown;
}
GsmConsolekit *
diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 1744706..26392e7 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -523,6 +523,9 @@ gsm_manager_quit (GsmManager *manager)
}
}
+static gboolean do_query_end_session_exit (GsmManager *manager);
+static void do_end_session_exit (GsmManager *manager);
+
static void
end_phase (GsmManager *manager)
{
@@ -564,9 +567,12 @@ end_phase (GsmManager *manager)
}
break;
case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+ if (!do_query_end_session_exit (manager))
+ start_next_phase = FALSE;
break;
case GSM_MANAGER_PHASE_END_SESSION:
maybe_save_session (manager);
+ do_end_session_exit (manager);
break;
case GSM_MANAGER_PHASE_EXIT:
start_next_phase = FALSE;
@@ -4079,3 +4085,64 @@ gsm_manager_is_session_running (GsmManager *manager,
return TRUE;
}
+static void
+on_shutdown_prepared (GsmSystem *system,
+ gboolean success,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: on_shutdown_prepared, success: %d", success);
+ g_signal_handlers_disconnect_by_func (system, on_shutdown_prepared, manager);
+
+ if (success) {
+ /* move to end-session phase */
+ end_phase (manager);
+ } else {
+ disconnect_shell_dialog_signals (manager);
+ gsm_shell_close_end_session_dialog (manager->priv->shell);
+ /* back to running phase */
+ cancel_end_session (manager);
+ }
+}
+
+static gboolean
+do_query_end_session_exit (GsmManager *manager)
+{
+ int action;
+
+ switch (manager->priv->logout_type) {
+ case GSM_MANAGER_LOGOUT_LOGOUT:
+ action = GSM_LOGOUT_ACTION_LOGOUT;
+ break;
+ case GSM_MANAGER_LOGOUT_REBOOT:
+ case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+ case GSM_MANAGER_LOGOUT_REBOOT_GDM:
+ action = GSM_LOGOUT_ACTION_REBOOT;
+ break;
+ case GSM_MANAGER_LOGOUT_SHUTDOWN:
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_GDM:
+ action = GSM_LOGOUT_ACTION_SHUTDOWN;
+ break;
+ default:
+ g_warning ("Unexpected logout type %d in do_query_end_session_exit()",
+ manager->priv->logout_type);
+ action = GSM_LOGOUT_ACTION_LOGOUT;
+ break;
+ }
+
+ if (action == GSM_LOGOUT_ACTION_REBOOT ||
+ action == GSM_LOGOUT_ACTION_SHUTDOWN) {
+ g_signal_connect (manager->priv->system, "shutdown-prepared",
+ G_CALLBACK (on_shutdown_prepared), manager);
+ gsm_system_prepare_shutdown (manager->priv->system, action == GSM_LOGOUT_ACTION_REBOOT);
+ return FALSE; /* don't leave query end session yet */
+ }
+
+ return TRUE; /* go to end session phase */
+}
+
+static void
+do_end_session_exit (GsmManager *manager)
+{
+ gsm_system_complete_shutdown (manager->priv->system);
+}
diff --git a/gnome-session/gsm-shell.c b/gnome-session/gsm-shell.c
index 87ff10b..450ca40 100644
--- a/gnome-session/gsm-shell.c
+++ b/gnome-session/gsm-shell.c
@@ -689,3 +689,12 @@ gsm_shell_open_end_session_dialog (GsmShell *shell,
return TRUE;
}
+
+void
+gsm_shell_close_end_session_dialog (GsmShell *shell)
+{
+ if (!shell->priv->end_session_dialog_proxy)
+ return;
+
+ dbus_g_proxy_call_no_reply (shell->priv->end_session_dialog_proxy, "Close", G_TYPE_INVALID);
+}
diff --git a/gnome-session/gsm-shell.h b/gnome-session/gsm-shell.h
index 785236a..77c8f03 100644
--- a/gnome-session/gsm-shell.h
+++ b/gnome-session/gsm-shell.h
@@ -82,6 +82,7 @@ gboolean gsm_shell_is_running (GsmShell *shell);
gboolean gsm_shell_open_end_session_dialog (GsmShell *shell,
GsmShellEndSessionDialogType type,
GsmStore *inhibitors);
+void gsm_shell_close_end_session_dialog (GsmShell *shell);
G_END_DECLS
diff --git a/gnome-session/gsm-system.c b/gnome-session/gsm-system.c
index 49a4316..0be1bf7 100644
--- a/gnome-session/gsm-system.c
+++ b/gnome-session/gsm-system.c
@@ -28,7 +28,8 @@
#include "gsm-systemd.h"
enum {
- REQUEST_COMPLETED = 0,
+ REQUEST_COMPLETED,
+ SHUTDOWN_PREPARED,
LAST_SIGNAL
};
@@ -53,6 +54,14 @@ gsm_system_default_init (GsmSystemInterface *iface)
NULL, NULL, NULL,
G_TYPE_NONE,
1, G_TYPE_POINTER);
+ signals[SHUTDOWN_PREPARED] =
+ g_signal_new ("shutdown-prepared",
+ GSM_TYPE_SYSTEM,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmSystemInterface, shutdown_prepared),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
pspec = g_param_spec_boolean ("active",
"Active",
"Whether or not session is active",
@@ -169,6 +178,19 @@ gsm_system_is_active (GsmSystem *system)
return is_active;
}
+void
+gsm_system_prepare_shutdown (GsmSystem *system,
+ gboolean restart)
+{
+ GSM_SYSTEM_GET_IFACE (system)->prepare_shutdown (system, restart);
+}
+
+void
+gsm_system_complete_shutdown (GsmSystem *system)
+{
+ GSM_SYSTEM_GET_IFACE (system)->complete_shutdown (system);
+}
+
GsmSystem *
gsm_get_system (void)
{
diff --git a/gnome-session/gsm-system.h b/gnome-session/gsm-system.h
index 6941823..c1c0d76 100644
--- a/gnome-session/gsm-system.h
+++ b/gnome-session/gsm-system.h
@@ -49,6 +49,9 @@ struct _GsmSystemInterface
void (* request_completed) (GsmSystem *system,
GError *error);
+ void (* shutdown_prepared) (GsmSystem *system,
+ gboolean success);
+
gboolean (* can_switch_user) (GsmSystem *system);
gboolean (* can_stop) (GsmSystem *system);
gboolean (* can_restart) (GsmSystem *system);
@@ -66,6 +69,9 @@ struct _GsmSystemInterface
GsmInhibitorFlag flags);
void (* remove_inhibitor) (GsmSystem *system,
const gchar *id);
+ void (* prepare_shutdown) (GsmSystem *system,
+ gboolean restart);
+ void (* complete_shutdown)(GsmSystem *system);
};
enum _GsmSystemError {
@@ -110,6 +116,11 @@ void gsm_system_add_inhibitor (GsmSystem *system,
void gsm_system_remove_inhibitor (GsmSystem *system,
const gchar *id);
+void gsm_system_prepare_shutdown (GsmSystem *system,
+ gboolean restart);
+void gsm_system_complete_shutdown (GsmSystem *system);
+
+
G_END_DECLS
diff --git a/gnome-session/gsm-systemd.c b/gnome-session/gsm-systemd.c
index 98e9f27..68d8988 100644
--- a/gnome-session/gsm-systemd.c
+++ b/gnome-session/gsm-systemd.c
@@ -60,6 +60,9 @@ struct _GsmSystemdPrivate
gint inhibit_fd;
gboolean is_active;
+
+ gint delay_inhibit_fd;
+ gboolean prepare_for_shutdown_expected;
};
enum {
@@ -77,13 +80,23 @@ static void
drop_system_inhibitor (GsmSystemd *manager)
{
if (manager->priv->inhibit_fd != -1) {
- g_debug ("Dropping system inhibitor");
+ g_debug ("GsmSystemd: Dropping system inhibitor");
close (manager->priv->inhibit_fd);
manager->priv->inhibit_fd = -1;
}
}
static void
+drop_delay_inhibitor (GsmSystemd *manager)
+{
+ if (manager->priv->delay_inhibit_fd != -1) {
+ g_debug ("GsmSystemd: Dropping delay inhibitor");
+ close (manager->priv->delay_inhibit_fd);
+ manager->priv->delay_inhibit_fd = -1;
+ }
+}
+
+static void
gsm_systemd_finalize (GObject *object)
{
GsmSystemd *systemd = GSM_SYSTEMD (object);
@@ -100,6 +113,7 @@ gsm_systemd_finalize (GObject *object)
g_slist_free_full (systemd->priv->inhibitors, g_free);
}
drop_system_inhibitor (systemd);
+ drop_delay_inhibitor (systemd);
G_OBJECT_CLASS (gsm_systemd_parent_class)->finalize (object);
}
@@ -247,10 +261,16 @@ on_sd_source_changed (gpointer user_data)
self->priv->is_active = active;
g_object_notify (G_OBJECT (self), "active");
}
-
+
return TRUE;
}
+static void sd_proxy_signal_cb (GDBusProxy *proxy,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data);
+
static void
gsm_systemd_init (GsmSystemd *manager)
{
@@ -263,6 +283,7 @@ gsm_systemd_init (GsmSystemd *manager)
GsmSystemdPrivate);
manager->priv->inhibit_fd = -1;
+ manager->priv->delay_inhibit_fd = -1;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (bus == NULL)
@@ -283,6 +304,8 @@ gsm_systemd_init (GsmSystemd *manager)
g_clear_error (&error);
}
+ g_signal_connect (manager->priv->sd_proxy, "g-signal",
+ G_CALLBACK (sd_proxy_signal_cb), manager);
sd_pid_get_session (getpid (), &manager->priv->session_id);
@@ -532,7 +555,7 @@ gsm_systemd_is_login_session (GsmSystem *system)
res = sd_session_get_class (manager->priv->session_id, &session_class);
if (res < 0) {
- g_warning ("could not get session class: %s", strerror (-res));
+ g_warning ("Could not get session class: %s", strerror (-res));
return FALSE;
}
ret = (g_strcmp0 (session_class, "greeter") == 0);
@@ -743,6 +766,95 @@ gsm_systemd_remove_inhibitor (GsmSystem *system,
}
static void
+reboot_or_poweroff_done (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsmSystemd *systemd = user_data;
+ GVariant *result;
+ GError *error = NULL;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
+ res,
+ &error);
+
+ if (result == NULL) {
+ if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED)) {
+ g_warning ("Shutdown failed: %s", error->message);
+ }
+ g_error_free (error);
+ drop_delay_inhibitor (systemd);
+ g_debug ("GsmSystemd: shutdown preparation failed");
+ systemd->priv->prepare_for_shutdown_expected = FALSE;
+ g_signal_emit_by_name (systemd, "shutdown-prepared", FALSE);
+ } else {
+ g_variant_unref (result);
+ }
+}
+
+static void
+gsm_systemd_prepare_shutdown (GsmSystem *system,
+ gboolean restart)
+{
+ GsmSystemd *systemd = GSM_SYSTEMD (system);
+ GUnixFDList *fd_list;
+ GVariant *res;
+ GError *error = NULL;
+ gint idx;
+
+ g_debug ("GsmSystemd: prepare shutdown");
+
+ res = g_dbus_proxy_call_with_unix_fd_list_sync (systemd->priv->sd_proxy,
+ "Inhibit",
+ g_variant_new ("(ssss)",
+ "shutdown",
+ g_get_user_name (),
+ "Preparing to end the session",
+ "delay"),
+ 0,
+ G_MAXINT,
+ NULL,
+ &fd_list,
+ NULL,
+ &error);
+ if (res == NULL) {
+ g_warning ("Failed to get delay inhibitor: %s", error->message);
+ g_error_free (error);
+ g_signal_emit_by_name (systemd, "shutdown-prepared", FALSE);
+ return;
+ }
+
+ g_variant_get (res, "(h)", &idx);
+
+ systemd->priv->delay_inhibit_fd = g_unix_fd_list_get (fd_list, idx, NULL);
+
+ g_debug ("GsmSystemd: got delay inhibitor, fd = %d", systemd->priv->delay_inhibit_fd);
+
+ g_variant_unref (res);
+ g_object_unref (fd_list);
+
+ systemd->priv->prepare_for_shutdown_expected = TRUE;
+
+ g_dbus_proxy_call (systemd->priv->sd_proxy,
+ restart ? "Reboot" : "PowerOff",
+ g_variant_new ("(b)", TRUE),
+ 0,
+ G_MAXINT,
+ NULL,
+ reboot_or_poweroff_done,
+ systemd);
+}
+
+static void
+gsm_systemd_complete_shutdown (GsmSystem *system)
+{
+ GsmSystemd *systemd = GSM_SYSTEMD (system);
+
+ /* remove delay inhibitor, if any */
+ drop_delay_inhibitor (systemd);
+}
+
+static void
gsm_systemd_system_init (GsmSystemInterface *iface)
{
iface->can_switch_user = gsm_systemd_can_switch_user;
@@ -758,6 +870,8 @@ gsm_systemd_system_init (GsmSystemInterface *iface)
iface->is_login_session = gsm_systemd_is_login_session;
iface->add_inhibitor = gsm_systemd_add_inhibitor;
iface->remove_inhibitor = gsm_systemd_remove_inhibitor;
+ iface->prepare_shutdown = gsm_systemd_prepare_shutdown;
+ iface->complete_shutdown = gsm_systemd_complete_shutdown;
}
GsmSystemd *
@@ -773,6 +887,36 @@ gsm_systemd_new (void)
return manager;
}
+static void
+sd_proxy_signal_cb (GDBusProxy *proxy,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ GsmSystemd *systemd = user_data;
+ gboolean is_about_to_shutdown;
+
+ g_debug ("GsmSystemd: received logind signal: %s", signal_name);
+
+ if (g_strcmp0 (signal_name, "PrepareForShutdown") != 0) {
+ g_debug ("GsmSystemd: ignoring %s signal", signal_name);
+ return;
+ }
+
+ g_variant_get (parameters, "(b)", &is_about_to_shutdown);
+ if (!is_about_to_shutdown) {
+ g_debug ("GsmSystemd: ignoring %s signal since about-to-shutdown is FALSE", signal_name);
+ return;
+ }
+
+ if (systemd->priv->prepare_for_shutdown_expected) {
+ g_debug ("GsmSystemd: shutdown successfully prepared");
+ g_signal_emit_by_name (systemd, "shutdown-prepared", TRUE);
+ systemd->priv->prepare_for_shutdown_expected = FALSE;
+ }
+}
+
#else
GsmSystemd *
@@ -782,3 +926,4 @@ gsm_systemd_new (void)
}
#endif
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]