Multiseat, Fast-user-Switching

David Zeuthen david at fubar.dk
Thu Sep 21 21:23:07 PDT 2006


Hi all, 

sorry for the lag,

On Tue, 2006-09-19 at 15:11 +0200, Tim Dijkstra wrote:
> On Mon, 18 Sep 2006 21:46:28 +0200
> Fabian Steiner <lists at fabis-site.net> wrote:
> 
> > Unfortunately, there is still one feature missing, though. So far it
> > is not possible to assign proper permissions to hotpluggable devices
> > such as memory sticks and cameras.
> 
> Right, I'm not familiar with multi-seat, but in the case of
> fast-user-switching, you want to have the same of course. What is below
> is geared towards f-u-s. 
> 
> I think the solution to this has to be implemented in dbus and hal. If I
> find some time, I would be interested in implementing this. I have some
> ideas about it, but don't know to much (yet?) about the internals of
> hald and dbus, so would want to discuss a bit about it. 
> 
> For this (assigning policy based on X session) to work we need a few
> bits of information. 
> 
> 1) session-dbus needs to be able to tell which X-display it belongs to.
>    It can find this information in the DISPLAY environment variable. 
>    (I guess in multiseat setups DISPLAY is still unique).
> 2) Of course this information can not be trusted, so it has to be verified.
>    The only way I can think of, is checking if the user that is logged in 
>    on that DISPLAY corresponds to the user that owns the session-dbus.
>    This leaves the possibility that user is logged in multiple times
>    and fakes one of its own DISPLAYs, but that doesn't seem a problem.

One idea occurred to me at OLS while talking to ajax (of X.org fame) was
the following. Assume each X.org server on the system claims the name

 org.x.Server.<DISPLAY>

on the system bus. So if you have four seats in your system, you'd have

 org.x.Server.0.0
 org.x.Server.1.0
 org.x.Server.2.0
 org.x.Server.3.0

or something. A single object /org/x/Server is exported with the
interface org.x.Server.Instance that implements one method

 IsPidConnected (IN int pid, OUT boolean isConnected)

that simply returns TRUE if the given UNIX process id is connected to
the X server. This is pretty straightforward to implement.

Now, we'd then have a very simple process claim the name

 org.x.Server

on the system bus too. That system bus service will export a single
object /org/x/Server with the one interface org.x.Server that implements
a single method call

 GetDisplayForName (IN string uniqueSystemBusName, OUT string display)

that returns the DISPLAY (e.g. 0.0, 1.0, ...) for a given unique name of
the system message bus. 

Now, remember that unique names on the system message bus are unique for
the uptime of the system message bus, ie. for the entire system.

Specifically you can ask the system message bus for the pid of the
process claiming that name. So if someone calls GetDisplayForName
(":54.0"), we simply look up the pid and query all X servers on the bus
[1]. If the process is not connected to the X server we return a blank
string. Of course, this requires that our clients in the desktop session
actually initialize X (via GTK+ or Qt or whatever) but I think, by and
large, that assumption is valid.

(We also choose to disregard bizarre cases whereby one process is
connected to more than one X server.)

Implementation-wise we can cache the result to minimize the amount of
traffic from the x.org.Server service to all the real X servers. This is
possible only because we use the fact that system bus unique names
cannot not be recycled. The same would not hold true if we exported the
method 

 GetDisplayForPid (IN int pid, OUT string display)

because Unix PID's can be recycled. We should probably export both just
for completeness.

Brilliant, so now we can securely (as far as we trust the X.org server)
get the DISPLAY for a given system bus unique name via D-BUS. It's all
portable to all UNIX-like platforms on which X.org and D-Bus is
available too. I think it would be awesome if X.org could provide this
functionality. I've copied Adam Jackson (ajax) to hear what he thinks..
I haven't thought it out too much though, maybe it's really crack.

[1] : It know what these are because it starts before all X servers and
listens to NameOwnerChanged signals from the system bus. Alternatively
it uses other mechanisms to discover the org.X.Server.<DISPLAY> names.
That's more an implementation detail though. Perhaps the first X server
to start claims the org.X.Server name and if it exits the others race to
get it. Rinse and repeat...

> 3) Check what virtual terminal (VT) the DISPLAY is on. This can be done
>    by checking the XFree86_VT root window property in the X server running
>    on DISPLAY.
> 4) Check which VT is active. For that we have VT_GETSTATE console ioctl.
> 
> I'm not sure where 2-4 would be most sensibly implemented. I would think
> dbus, the problem however is that dbus runs unprivileged and we need
> root for 2-4....
> 
> I realize 3, 4 are specific to f-u-s. 
> 
> I wonder how the server in a multi-seat setup is aware of for example
> the USB ports on the thin client. It seems logical that there's some
> connection between DISPLAY and the devices on the thin client. For f-u-s
> all DISPLAYs know about all devices, obviously. So we also need:
> 
> 5) Hal should know about connection DISPLAY <-> device.
>    Maybe implemented through some fdi files?
> 
> 
> With this information, we can have the following scenario:
> a) user plugs in USB key
> b) hal gets kernel event.
> c) hal announces new device available on system bus.
> e) g-v-m on $DISPLAY asks hal to mount the key
> f) hal verifies that $DISPLAY of session-dbus is authorized for device
> g) hal verifies that owner of session-dbus is on active VT
> 
> Here f) would be irrelevant for f-u-s and g) irrelevant for multi-seat.
> On could also imagine that c) is changed so that hal only announces on
> session-dbus' of which it knows they are authorized. You still would
> need verification though.
> 
> 
> Comments anyone?

Right. Some random thought: One idea I've been toying with is a
org.fd.ConsoleKit service on the system messages that provides a very
simple, yet powerful, piece of information, namely

 - what seats are available
   - async signals when seats are added / removed
   - for each seat 
     - what devices are available for the seat
       - probably just expressed as HAL UDI's 
         - same UDI can appear for multiple seats - allow seats 1-5 to
           use CD-RW device A and seats 6-9 to use CD-RW device B
         - typically just the HAL UDI of the videocard and a USB hub
     - what sessions are on each seat
       - async signals when sessions are started / stopped
       - async signals session comes to foreground / background
       - for each session
         - is it a login window (e.g. gdm or login(1) or an user
           session, e.g. bash on a VT, GNOME or KDE and so on?
         - is a session active (e.g. in the foreground?)
         - is it GNOME, KDE or bash
         - something like the DISPLAY variable

plus some lookup methods like GetSessionIdForX11Display() to e.g. get a
Session object from a DISPLAY variable.

Expressed as D-Bus objects this would be

 /org/freedesktop/ConsoleKit/seat1
  /org/freedesktop/ConsoleKit/session1, seat=0, active=0, usage="loginmanager", type="login(1)", uid=0
  /org/freedesktop/ConsoleKit/session2, seat=0, active=0, usage="loginmanager", type="login(1)", uid=0
  /org/freedesktop/ConsoleKit/session3, seat=0, active=0, usage="loginmanager", type="login(1)", uid=0
  /org/freedesktop/ConsoleKit/session4, seat=0, active=0, usage="loginmanager", type="login(1)", uid=0
  /org/freedesktop/ConsoleKit/session5, seat=0, active=0, usage="usersession", type="bash", uid=500
  /org/freedesktop/ConsoleKit/session6, seat=0, active=0, usage="loginmanager", type="login(1)", uid=0
  /org/freedesktop/ConsoleKit/session7, seat=0, active=1, usage="usersession", type="gnome-session", uid=500
  /org/freedesktop/ConsoleKit/session8, seat=0, active=0, usage="usersession", type="gnome-session", uid=501

I think this could even be portable though I haven't yet started to
think about the implementation details. I know Jon William Mccann and
Richard Hughes have been looking into stuff like this too.

Another thing, I'm not sure how to integrate HAL with this. We could
just punt it to things like g-v-m to ask ConsoleKit about devices but
that wouldn't be very nice. 

I think the right thing is to decorate each HAL device with a new
property

 info.seat (strlist)

that contains the identifier for the seats (plural) a device belongs to.

We'd get this by looking at the configured seats and we'd be using
inheritance from the parent if a device isn't mentioned for a seat (e.g.
if a seat owns a USB hub all devices hanging off this in the tree will
have the same info.seat value).

Can probably handle reconfiguration on the fly too, that's just
listening to signals from ConsoleKit. Might be a bit expensive, dunno,
it's not something that happens very often.

Then when someone calls a method on HAL, we'd

 - determine DISPLAY of caller (via org.X.Server service)
   (and we can cache this in HAL)
 - determine Session of caller (via org.freedesktop.ConsoleKit)
   (and we can cache this in HAL)
 - compare to info.seat to determine device ownership
 - see what privileges we need (ties in with PolicyKit, most actions
   would probably be restricted to only active sessions, others might
   be allowed even if you are an inactive session)

Regarding seats. This, I believe, is kinda tricky to autodetect, I mean
there is no way to make a distinction between the two setups

1. David likes his desktop to stretch across his two flat panels. He
   has two keyboards, a Danish and an English one. He also uses two
   mice because he is weird.

2. Ben & Jerry share a single computer with two video cards and two
   USB hubs. Each video card got a monitor hooked up and they have
   separate hubs with keyboard/mice plugged in.

So some degree of configuration is required. Maybe we don't want to
support multiseat proper in the first release, but at least the API
should support it. Personally I think f-u-s is the most interesting case
to get right but I do realize multiseat is very important for some
people too. Ideally we'd do both.

This all ties in very much with PolicyKit - right now we have a lame pam
module to determine if a user is on the console. We can do much better.
In fact it might make sense for the ConsoleKit bits to live in
PolicyKit.

Not sure how this ties in with LTSP stuff and SunRay stuff. I think both
instances would somehow just register with ConsoleKit too. Ideally our
design should cover this too.

What do you think?

    David




More information about the hal mailing list