[systemd-devel] [PATCH] logind: implement generic multi-session

2013-09-17 Thread David Herrmann
This enables the multi-session capability for seats that don't have VTs.
For legacy seats with VTs, everything stays the same. However, all other
seats now also get the multi-session capability.

The only feature that was missing was session-switching. As logind can
force a session-switch and signal that via the Active property, we only
need a way to allow synchronized/delayed session switches. Compositors
need to cleanup some devices before acknowledging the session switch.
Therefore, we use the session-devices to give compositors a chance to
block a session-switch until they cleaned everything up.

If you activate a session on a seat without VTs, we send a PauseDevice
signal to the active session for every active device. Only once the
session acknowledged all these with a PauseDeviceComplete() call, we
perform the final session switch.

One important note is that delayed session-switching is meant for
backwards compatibility. New compositors or other sessions should really
try to deal correctly with forced session switches! They only need to
handle EACCES/EPERM from syscalls and treat them as PauseDevice signal.

Following logind patches will add a timeout to session-switches which
forces the switch if the active session does not react in a timely
fashion. Moreover, explicit ForceActivate() calls might also be supported.
Hence, sessions must not crash if their devices get paused.
---
 src/login/logind-seat.c   | 15 +++
 src/login/logind-seat.h   |  2 ++
 src/login/logind-session-device.c | 28 
 src/login/logind-session-device.h |  1 +
 src/login/logind-session.c| 31 ++-
 5 files changed, 72 insertions(+), 5 deletions(-)

diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index f88738a..4a4d40a 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -425,6 +425,21 @@ int seat_attach_session(Seat *s, Session *session) {
 return 0;
 }
 
+void seat_complete_switch(Seat *s) {
+Session *session;
+
+assert(s);
+
+/* if no session-switch is pending or if it got canceled, do nothing */
+if (!s-pending_switch)
+return;
+
+session = s-pending_switch;
+s-pending_switch = NULL;
+
+seat_set_active(s, session);
+}
+
 bool seat_has_vts(Seat *s) {
 assert(s);
 
diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h
index d3438b8..be6db6e 100644
--- a/src/login/logind-seat.h
+++ b/src/login/logind-seat.h
@@ -38,6 +38,7 @@ struct Seat {
 LIST_HEAD(Device, devices);
 
 Session *active;
+Session *pending_switch;
 LIST_HEAD(Session, sessions);
 
 bool in_gc_queue:1;
@@ -59,6 +60,7 @@ int seat_read_active_vt(Seat *s);
 int seat_preallocate_vts(Seat *s);
 
 int seat_attach_session(Seat *s, Session *session);
+void seat_complete_switch(Seat *s);
 
 bool seat_has_vts(Seat *s);
 bool seat_is_seat0(Seat *s);
diff --git a/src/login/logind-session-device.c 
b/src/login/logind-session-device.c
index 80fd364..e92bb54 100644
--- a/src/login/logind-session-device.c
+++ b/src/login/logind-session-device.c
@@ -414,10 +414,21 @@ void session_device_free(SessionDevice *sd) {
 }
 
 void session_device_complete_pause(SessionDevice *sd) {
+SessionDevice *iter;
+Iterator i;
+
 if (!sd-active)
 return;
 
 session_device_stop(sd);
+
+/* if not all devices are paused, wait for further completion events */
+HASHMAP_FOREACH(iter, sd-session-devices, i)
+if (iter-active)
+return;
+
+/* complete any pending session switch */
+seat_complete_switch(sd-session-seat);
 }
 
 void session_device_resume_all(Session *s) {
@@ -449,3 +460,20 @@ void session_device_pause_all(Session *s) {
 }
 }
 }
+
+unsigned int session_device_try_pause_all(Session *s) {
+SessionDevice *sd;
+Iterator i;
+unsigned int num_pending = 0;
+
+assert(s);
+
+HASHMAP_FOREACH(sd, s-devices, i) {
+if (sd-active) {
+session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
+++num_pending;
+}
+}
+
+return num_pending;
+}
diff --git a/src/login/logind-session-device.h 
b/src/login/logind-session-device.h
index 511fce0..eac7ca7 100644
--- a/src/login/logind-session-device.h
+++ b/src/login/logind-session-device.h
@@ -57,3 +57,4 @@ void session_device_complete_pause(SessionDevice *sd);
 
 void session_device_resume_all(Session *s);
 void session_device_pause_all(Session *s);
+unsigned int session_device_try_pause_all(Session *s);
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index fcc1901..eea0bfb 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -100,6 +100,8 @@ void session_free(Session *s) {
 if (s-seat) {
  

Re: [systemd-devel] [PATCH] logind: implement generic multi-session

2013-09-17 Thread Lennart Poettering
On Tue, 17.09.13 23:40, David Herrmann (dh.herrm...@gmail.com) wrote:

Applied both! Thanks a lot!

 This enables the multi-session capability for seats that don't have VTs.
 For legacy seats with VTs, everything stays the same. However, all other
 seats now also get the multi-session capability.
 
 The only feature that was missing was session-switching. As logind can
 force a session-switch and signal that via the Active property, we only
 need a way to allow synchronized/delayed session switches. Compositors
 need to cleanup some devices before acknowledging the session switch.
 Therefore, we use the session-devices to give compositors a chance to
 block a session-switch until they cleaned everything up.
 
 If you activate a session on a seat without VTs, we send a PauseDevice
 signal to the active session for every active device. Only once the
 session acknowledged all these with a PauseDeviceComplete() call, we
 perform the final session switch.
 
 One important note is that delayed session-switching is meant for
 backwards compatibility. New compositors or other sessions should really
 try to deal correctly with forced session switches! They only need to
 handle EACCES/EPERM from syscalls and treat them as PauseDevice signal.
 
 Following logind patches will add a timeout to session-switches which
 forces the switch if the active session does not react in a timely
 fashion. Moreover, explicit ForceActivate() calls might also be supported.
 Hence, sessions must not crash if their devices get paused.
 ---
  src/login/logind-seat.c   | 15 +++
  src/login/logind-seat.h   |  2 ++
  src/login/logind-session-device.c | 28 
  src/login/logind-session-device.h |  1 +
  src/login/logind-session.c| 31 ++-
  5 files changed, 72 insertions(+), 5 deletions(-)
 
 diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
 index f88738a..4a4d40a 100644
 --- a/src/login/logind-seat.c
 +++ b/src/login/logind-seat.c
 @@ -425,6 +425,21 @@ int seat_attach_session(Seat *s, Session *session) {
  return 0;
  }
  
 +void seat_complete_switch(Seat *s) {
 +Session *session;
 +
 +assert(s);
 +
 +/* if no session-switch is pending or if it got canceled, do nothing 
 */
 +if (!s-pending_switch)
 +return;
 +
 +session = s-pending_switch;
 +s-pending_switch = NULL;
 +
 +seat_set_active(s, session);
 +}
 +
  bool seat_has_vts(Seat *s) {
  assert(s);
  
 diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h
 index d3438b8..be6db6e 100644
 --- a/src/login/logind-seat.h
 +++ b/src/login/logind-seat.h
 @@ -38,6 +38,7 @@ struct Seat {
  LIST_HEAD(Device, devices);
  
  Session *active;
 +Session *pending_switch;
  LIST_HEAD(Session, sessions);
  
  bool in_gc_queue:1;
 @@ -59,6 +60,7 @@ int seat_read_active_vt(Seat *s);
  int seat_preallocate_vts(Seat *s);
  
  int seat_attach_session(Seat *s, Session *session);
 +void seat_complete_switch(Seat *s);
  
  bool seat_has_vts(Seat *s);
  bool seat_is_seat0(Seat *s);
 diff --git a/src/login/logind-session-device.c 
 b/src/login/logind-session-device.c
 index 80fd364..e92bb54 100644
 --- a/src/login/logind-session-device.c
 +++ b/src/login/logind-session-device.c
 @@ -414,10 +414,21 @@ void session_device_free(SessionDevice *sd) {
  }
  
  void session_device_complete_pause(SessionDevice *sd) {
 +SessionDevice *iter;
 +Iterator i;
 +
  if (!sd-active)
  return;
  
  session_device_stop(sd);
 +
 +/* if not all devices are paused, wait for further completion events 
 */
 +HASHMAP_FOREACH(iter, sd-session-devices, i)
 +if (iter-active)
 +return;
 +
 +/* complete any pending session switch */
 +seat_complete_switch(sd-session-seat);
  }
  
  void session_device_resume_all(Session *s) {
 @@ -449,3 +460,20 @@ void session_device_pause_all(Session *s) {
  }
  }
  }
 +
 +unsigned int session_device_try_pause_all(Session *s) {
 +SessionDevice *sd;
 +Iterator i;
 +unsigned int num_pending = 0;
 +
 +assert(s);
 +
 +HASHMAP_FOREACH(sd, s-devices, i) {
 +if (sd-active) {
 +session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
 +++num_pending;
 +}
 +}
 +
 +return num_pending;
 +}
 diff --git a/src/login/logind-session-device.h 
 b/src/login/logind-session-device.h
 index 511fce0..eac7ca7 100644
 --- a/src/login/logind-session-device.h
 +++ b/src/login/logind-session-device.h
 @@ -57,3 +57,4 @@ void session_device_complete_pause(SessionDevice *sd);
  
  void session_device_resume_all(Session *s);
  void session_device_pause_all(Session *s);
 +unsigned int session_device_try_pause_all(Session *s);
 diff