chiark / gitweb /
login: pause devices before acknowledging VT switches
authorDavid Herrmann <dh.herrmann@gmail.com>
Fri, 19 Sep 2014 11:26:39 +0000 (13:26 +0200)
committerDavid Herrmann <dh.herrmann@gmail.com>
Fri, 19 Sep 2014 11:26:39 +0000 (13:26 +0200)
If a session controller does not need synchronous VT switches, we allow
them to pass VT control to logind, which acknowledges all VT switches
unconditionally. This works fine with all sessions using the dbus API,
but causes out-of-sync device use if we switch to legacy sessions that
are notified via VT signals. Those are processed before logind notices
the session-switch via sysfs. Therefore, leaving the old session still
active for a short amount of time.

This, in fact, may cause the legacy session to prepare graphics devices
before the old session was deactivated, and thus, maybe causing the old
session to interfer with graphics device usage.

Fix this by releasing devices immediately before acknowledging VT
switches. This way, sessions without VT handlers are required to support
async session switching (which they do in that case, anyway).

src/login/logind-session.c
src/login/logind-session.h
src/login/logind.c

index eeb58c9031b71d1bce41805cb778e6d4c7890d30..477ac9ab1b931a686386a51ffc064923d78cd060 100644 (file)
@@ -1053,6 +1053,27 @@ void session_restore_vt(Session *s) {
         s->vtfd = safe_close(s->vtfd);
 }
 
+void session_leave_vt(Session *s) {
+        assert(s);
+
+        /* This is called whenever we get a VT-switch signal from the kernel.
+         * We acknowledge all of them unconditionally. Note that session are
+         * free to overwrite those handlers and we only register them for
+         * sessions with controllers. Legacy sessions are not affected.
+         * However, if we switch from a non-legacy to a legacy session, we must
+         * make sure to pause all device before acknowledging the switch. We
+         * process the real switch only after we are notified via sysfs, so the
+         * legacy session might have already started using the devices. If we
+         * don't pause the devices before the switch, we might confuse the
+         * session we switch to. */
+
+        if (s->vtfd < 0)
+                return;
+
+        session_device_pause_all(s);
+        ioctl(s->vtfd, VT_RELDISP, 1);
+}
+
 bool session_is_controller(Session *s, const char *sender) {
         assert(s);
 
index 9fb0188a8478ee16086e2622c61f4a38246b18b2..a007fb5e84cf468fe02df363ce0e4af94bb04eb2 100644 (file)
@@ -174,6 +174,7 @@ KillWho kill_who_from_string(const char *s) _pure_;
 
 int session_prepare_vt(Session *s);
 void session_restore_vt(Session *s);
+void session_leave_vt(Session *s);
 
 bool session_is_controller(Session *s, const char *sender);
 int session_set_controller(Session *s, const char *sender, bool force);
index f1b6a86298bd5b1237469c8c6f60292da094172b..8f00c463399bc951015e98e0856e348d08300224 100644 (file)
@@ -750,11 +750,11 @@ static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo
         }
 
         if (active->vtfd >= 0) {
-                ioctl(active->vtfd, VT_RELDISP, 1);
+                session_leave_vt(active);
         } else {
                 LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) {
                         if (iter->vtnr == active->vtnr && iter->vtfd >= 0) {
-                                ioctl(iter->vtfd, VT_RELDISP, 1);
+                                session_leave_vt(iter);
                                 break;
                         }
                 }