[28293] in Source-Commits
reactivate commit: Add reactivate-helper to query logind
daemon@ATHENA.MIT.EDU (Jonathan D Reed)
Sat Jul 5 11:57:54 2014
Date: Sat, 5 Jul 2014 11:57:47 -0400
From: Jonathan D Reed <jdreed@MIT.EDU>
Message-Id: <201407051557.s65Fvlk6032406@drugstore.mit.edu>
To: source-commits@MIT.EDU
https://github.com/mit-athena/reactivate/commit/d2b173df10698623a45fe9fa5a720c5a21e1fdb4
commit d2b173df10698623a45fe9fa5a720c5a21e1fdb4
Author: Jonathan Reed <jdreed@mit.edu>
Date: Tue Jul 1 15:44:27 2014 -0400
Add reactivate-helper to query logind
- Add a "reactivate-helper" script, and use it in the session
teardown to determine whether or not we should reactivate.
debian/01debathena-reactivate-cleanup | 26 +++++-
debian/changelog | 5 +-
debian/debathena-reactivate.install | 1 +
debian/reactivate-helper | 138 +++++++++++++++++++++++++++++++++
4 files changed, 165 insertions(+), 5 deletions(-)
diff --git a/debian/01debathena-reactivate-cleanup b/debian/01debathena-reactivate-cleanup
index 97422dd..c5964cb 100644
--- a/debian/01debathena-reactivate-cleanup
+++ b/debian/01debathena-reactivate-cleanup
@@ -4,9 +4,27 @@
# This script is also sourced as root at the end of a lightdm login
# session
-# Set the volume to zero for all sound cards, and save that state.
-invoke-rc.d debathena-reactivate start
+do_reactivate() {
+ # Set the volume to zero for all sound cards, and save that state.
+ invoke-rc.d debathena-reactivate start
-/usr/lib/debathena-reactivate/reactivate
+ /usr/lib/debathena-reactivate/reactivate
-rm /var/run/athena-login
+ rm /var/run/athena-login
+}
+
+HELPER=/usr/lib/debathena-reactivate/reactivate-helper
+
+if ! $HELPER supported; then
+ # We can't check; just assume we should (legacy behavior)
+ do_reactivate
+else
+ if $HELPER should-reactivate; then
+ # We should reactivate, so do it
+ do_reactivate
+ elif [ $? -ne 1 ]; then
+ # An error occurred while determining if we should reactivate
+ # Fail-safe and reactivate anyway (at risk of the user losing data)
+ do_reactivate
+ fi
+fi
diff --git a/debian/changelog b/debian/changelog
index bca4eaf..b39670b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,8 @@
debathena-reactivate (2.0.47) UNRELEASED; urgency=low
+ * Add new reactivate-helper script to query logind and decide whether or
+ not to reactivate. In particular, this prevents spontaneous reboots
+ when user-switching is enabled.
* Ensure the dbus-inhibitor file is gone when the chroot starts up.
(Ending stale chroots will cause the file to stick around)
(Trac: #1497)
@@ -8,7 +11,7 @@ debathena-reactivate (2.0.47) UNRELEASED; urgency=low
chroot, because gvfs keeps something open that causes the regular
umount to return EBUSY. (Trac: #1500)
- -- Jonathan Reed <jdreed@mit.edu> Mon, 23 Jun 2014 16:56:11 -0400
+ -- Jonathan Reed <jdreed@mit.edu> Wed, 02 Jul 2014 13:07:57 -0400
debathena-reactivate (2.0.46) unstable; urgency=low
diff --git a/debian/debathena-reactivate.install b/debian/debathena-reactivate.install
index ab88ad5..6cf4f6f 100644
--- a/debian/debathena-reactivate.install
+++ b/debian/debathena-reactivate.install
@@ -20,3 +20,4 @@ debian/11umount-afs etc/schroot/setup.d
debian/dbus-daemon-launch-helper-blacklist usr/share/debathena-reactivate
debian/debathena-reactivate-sudoers etc/sudoers.d
command-not-found.mo usr/share/locale/en/LC_MESSAGES
+debian/reactivate-helper usr/lib/debathena-reactivate
diff --git a/debian/reactivate-helper b/debian/reactivate-helper
new file mode 100755
index 0000000..38cc8fa
--- /dev/null
+++ b/debian/reactivate-helper
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+#
+# Query logind to decide whether or not reactivation is needed.
+# Returns 0 or 1 to indicate true or false answers to
+# its invocation. Returns 127 if the world exploded.
+
+import os
+import sys
+import time
+
+from collections import defaultdict
+
+import dbus
+from dbus.exceptions import DBusException
+
+LD_NAME = 'org.freedesktop.login1'
+LD_MANAGER_PATH = '/org/freedesktop/login1'
+LD_MANAGER_IFACE = 'org.freedesktop.login1.Manager'
+LD_SESSION_IFACE = 'org.freedesktop.login1.Session'
+
+SERVICE_UNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown'
+
+def get_sessions():
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object(LD_NAME, LD_MANAGER_PATH),
+ LD_MANAGER_IFACE)
+ target_sessions = defaultdict(list)
+ # Get all active sessions
+ sessions = manager.ListSessions()
+ for s in sessions:
+ # Each session is a dbus.Struct (tuple) of:
+ # (session id, uid, username, seat id, and session object path)
+ session = dbus.Interface(bus.get_object(LD_NAME, s[-1]),
+ LD_SESSION_IFACE)
+ obj_properties = dbus.Interface(session,
+ 'org.freedesktop.DBus.Properties')
+ properties = obj_properties.GetAll(LD_SESSION_IFACE)
+ target_sessions[properties['Class'].lower()].append(properties)
+ return target_sessions
+
+def log_sessions(sessions):
+ with open("/var/log/reactivate-helper.log", "a") as f:
+ f.write("------ Log begins: {0} -----\n".format(time.ctime()))
+ for s_type in sessions:
+ for s in sessions[s_type]:
+ for k,v in s.items():
+ f.write(" {0} = {1}\n".format(k,v))
+ f.write("----\n")
+ f.write("------ Log ends -----\n")
+
+def should_reactivate(args=None):
+ """Determine if we should reactivate or not.
+
+ Part of this is a workaround for LP #1249515, in which
+ session-cleanup now gets called when authentication is cancelled,
+ but part of it is to not reboot out from under the user when they
+ manage to activate user-switching and attempt to reconnect to
+ their session.
+ """
+ sessions = get_sessions()
+ # If there are multiple of either kind, we have no idea how to deal
+ # The calling code can decide how to handle errors
+ if len(sessions['greeter']) > 1:
+ log_sessions(sessions)
+ raise ValueError("Too many greeter sessions")
+ if len(sessions['user']) > 1:
+ log_sessions(sessions)
+ raise ValueError("Too many user sessions")
+ if len(sessions['greeter']) == 0 and \
+ len(sessions['user']) == 0:
+ # If there are no greeter sessions, and no
+ # user sessions, reactivate
+ return True
+ if len(sessions['greeter']) > 0 and \
+ len(sessions['user']) == 0:
+ # If there is a greeter session and no user session...
+ if sessions['greeter'][0]['State'] == 'active':
+ # The user hit 'Cancel', probably
+ return False
+ elif sessions['greeter'][0]['State'] == 'online':
+ # We just came back to a second greeter from an ending
+ # login session, and should reactivate
+ return True
+ if len(sessions['greeter']) > 0 and \
+ len(sessions['user']) > 0:
+ # If there are both sessions
+ if sessions['user'][0]['State'] == 'closing':
+ # If the user session is closing, the user managed to pick
+ # "Shutdown" from the second greeter's login screen.
+ # do _not_ reactivate, since we'll likely end up with stray
+ # processes.
+ return False
+ if sessions['greeter'][0]['State'] == 'active' and \
+ sessions['user'][0]['State'] == 'online':
+ # The user hit 'Cancel' at a second greeter
+ return False
+ if sessions['greeter'][0]['State'] == 'closing' and \
+ sessions['user'][0]['State'] == 'active':
+ # The user spawned a second greeter, switched back to
+ # their original VT, unlocked it, then switched to the
+ # second greeter's VT, and attempted to unlock the screen
+ # from the greeter. (WHY??!)
+ return False
+ if len(sessions['user']) > 0 and \
+ len(sessions['greeter']) == 0:
+ # A user session is active
+ if sessions['user'][0]['State'] == 'active':
+ # If there's a single active user session, we just re-authenticated
+ # to a previously locked session, so do not reactivate
+ return False
+ log_sessions(sessions)
+ raise ValueError("No idea how to handle this situation")
+
+def supported(args=None):
+ try:
+ _ = dbus.SystemBus().get_object(LD_NAME, LD_MANAGER_PATH)
+ return True
+ except DBusException as e:
+ if e.get_dbus_name() != SERVICE_UNKNOWN:
+ raise e
+ return False
+
+if __name__ == "__main__":
+ dispatch = { 'supported': supported,
+ 'should-reactivate': should_reactivate }
+ if len(sys.argv) < 2 or sys.argv[1] not in dispatch:
+ print >>sys.stderr, "Usage: {progname} {cmds}".format(
+ progname=os.path.basename(sys.argv[0]),
+ cmds='|'.join(dispatch.keys()))
+ sys.exit(127)
+ try:
+ if dispatch[sys.argv[1]](sys.argv[2:]):
+ sys.exit(0)
+ else:
+ sys.exit(1)
+ except Exception as e:
+ print >>sys.stderr, "Unexpected Exception:", e
+ sys.exit(127)