[systemd-devel] RFC: User sessions in systemd: `systemd --user` - code and experiences

Kok, Auke-jan H auke-jan.h.kok at intel.com
Thu Apr 12 16:12:43 PDT 2012


All,

As some of you might know, I've been working on integrating systemd in
Tizen. While currently the Tizen OS is still using sysvinit, we hope
to merge systemd in the near future and even expand the use of systemd
throughout the system beyond what systemd currently brings. I will be
presenting a talk at the Tizen Conference in May in San Francisco on
this topic as well.

We have been working with our Samsung colleagues to define the goal
forward and I have personally been working on implementing
systemd-based user sessions. This post is a first public disclosure of
my efforts so far. The goal of this post is to take some time to
document what we did, where I think we should be going and what needs
to be done to make user sessions built on `systemd --user` more
mature.

First of all, some history. Before systemd, every OS has had to
provide lots of glue to go from "the system booted" to "the session
has started". The key difference is that starting system services is
an entirely different domain than the user session. Fortunately, the
differences in how things are actually started are almost the same, so
obviously we want to use the exact same mechanisms for controlling
user session programs.

Before systemd, every desktop had to implement some form of
'xinit/startx' of their own, or rely on a login manager to do the
right thing. Neither methods are the right solution - some desktops
will not need functionality that gdm provides, and some need more,
resulting in programs such as gnome-session, startxfce4 etc. to do
additional and duplicate work. None of this work is portable,
resulting in islands of solutions that don't scale, work differently
etc.

One solution we came up with at Intel when we were developing a new UI
based on clutter was to abstract the session startup into a generic
component. The major reason to do this was to eliminate gdm, as we
were (and are still) targeting single-user type systems. The end
solution was "uxlaunch". Uxlaunch provided us with the base bits to
start any arbitrary UI including gnome, xfce, the MeeGo UI with little
to no work.

Now, with systemd entering the picture, one of the biggest parts of
uxlaunch is duplicating something that systemd already does best:
starting and controlling services. This part is what used to handle
XDG autostart. The obvious decision we should take is to remove all
that functionality and instead use `systemd --user`. Effectively,
that's what we did and I forked the project off to implement a
prototype desktop startup sequence based on the remaining core code
that does a few basic bits that we still need to do for now.

The new project is boringly called "user-session" and I've posted an
initial working release tarball at
http://foo-projects.org/~sofar/user-session/ .


Here is what user-session currently (roughly) implements:

1) Basic PAM login conversation so pam_systemd.so's bits are used to
do things for us - this will help us get logind support do the right
thing for us for a lot of stuff we are going to need.

2) Currently user-session autologin's a user (hardcoded), but the
near-term plan is to make this program run as an instance service and
have systemd pass %i to the service unit, which user-session then can
get from argv[1] at run time. This will allow display login managers
to `systemctl start user-session at lennert.service` or something
similar.

A notable caveat is that I haven't thought out how this is going to
work well with seats just yet. We obviously want to allow GLM's(*) to
use systemd bits to start user sessions through some IPC method -
ideally, all it should have to do is register a new "seat" to the
system, and start operating on that seat presenting a "login"
facility. After a succesful authentication, it just needs to convey
the message to the system in some form that user X needs to have a
session started for him on this seat. I'm assuming that the logind
service will be able to register the needed information on the seat,
and at that point we can embed additional pieces of information into
the seat: seat -> user, seat -> DISPLAY, seat ->
DBUS_SESSION_BUS_ADDRESS, etc, etc.

(*) I'm reinventing the term Graphical Login Manager here, since I
think the term "Graphical Display Manager" is somewhat wrong since
"gdm" only manages a single display, but it manages multiple logins -
so personally the term GLM feels much more natural to me.

3) user-session drops privileges and setuid()'s to the user.

4) user-session will run a shell, and ask it to parse all it's default
shell setting initialization scripts, and dump the environment out. We
parse the environment and include all the settings. While somewhat
expensive and cumbersome, this is by far the easiest way to ensure we
can provide users with some compatible way of including basic shell
settings. Things like `alias` won't work, nor will having csh as shell
work, it's just a convenient hack for now.

5) user-session sets 'XDG_RUNTIME_DIR' to "/run/user/$USER". This will
facilitate us creating session-based sockets and use this folder for
temp data.

6) user-session creates an Xauth cookie for use with Xorg. I'm not
even sure we need this at all, but we had the code so that was easy
enough to leave in place.

7) user-session sets DBUS_SESSION_BUS_ADDRESS and points it at a
non-existant socket in $XDG_RUNTIME_DIR. user-session does not launch
a dbus session, but we do have a dbus.socket file that allows systemd
to socket-activate a dbus-daemon for the --session at this address,
which completes the circle. We do however need to provide the
environment since that will be the only way we can easily make sure
applications know where the session bus will be.

8) user-session launches Xorg, and waits for it to send the 'socket
ready' message back. Obviously, we would love for Xorg to become
socket activated in the future, but given the short time I had to
write this code, I left it as is since it should be easy to migrate to
something better. Making X socket activated should not be too hard,
but I don't think it's worth doing unless we can also move the socket
out of /tmp and into $XDG_RUNTIME_DIR where I think it belongs (**),
which means that we need to tell all the applications somehow where it
now lives. In systemd, that would mean that we have to know beforehand
where it is going to live since it will be socket activated on
request. I also need to have a good discussion with security and X
folks to see if this is worth doing in the first place, and whether we
would benefit from it security wise.

(**) Having sockets no longer in /tmp is a good thing, and, I really
want to use PrivateTmp=yes and have a per-user tmpfs there to further
separate and reduce attackable surface. As is, doing PrivateTmp= will
not work for anything that needs X access, and the entire premise
around user-session is that we are going to be starting UI components
in it, so they need access to it.

9) Last, user-session forks and exec's /usr/lib/systemd/systemd --user

In this last step, we effectively eliminate all xdg autostart code,
and provide unit files for all UI components and user services. We can
start things like pulseaudio servers, the session socket bus and what
not.

This new systemd instance will parse the unit files under
/usr/lib/systemd/user/ and, given the environment presented will have
DBUS_SESSION_BUS_ADDRESS and DISPLAY set, will be very much capable of
starting any UI - including gnome, enlightenment, xfce4, kde etc.
component by component.

In the /usr/lib/systemd/system there exists a default.target. We have
it symlinked to the default UI that the OS brings which is our Tizen
mobile UI, but a user could create
~/.config/systemd/user/default.target to override this and thus
configure their preferred UI, and for instance make it point to
"/usr/lib/systemd/system/gnome.target". In turn, gnome.target and
tizen.target could have dependencies for dbus.target, which requires
the 'dbus.socket'. The dbus.socket unit file (attached) will point
BOTH UI's to the dbus session bus socket in XDG_RUNTIME_DIR, so there
is a really easy way for different UI's to "borrow" functionality and
enable services that are needed for multiple UI's. One could see how
OnlyShowIn= and NotShowIn= keys are just horribly inefficient at doing
the same thing - in order to support a new UI, you had to formerly
modify the files which creates issues with rpm upgrades, but from now
on all one needs to do is create a symlink to a base service file from
within /usr/lib/systemd/user/new-ui-target.target.wants/.

On top of having a better way of doing autostart, we can start to
experiment with using some of the sandboxing and ""security"" features
that systemd has for UI components. Resource limitation is very
interesting, especially to limit rogue UI components. It opens up a
huge array of possibilities.

We can also create realistic targets grouping dependencies and start
working on creating working dependency models as part of the UI. One
could for instance create gnome-services.target, which will allow
non-gnome UI's to enable gnome-service support for a user by trivially
doing ln -sf /usr/lib/systemd/user/gnome-services.target
~/.config/systemd/user/my-ui.target.wants/gnome-services.target. It
all gets really consistent and convenient from there on.

A problem still existing is things like ssh-agent using (effectively)
obsolete environment-style addresses. We really need to change these
services to use DBUS or better methods. Alternatively, we should
change them so their sockets are under /run/user/$USER and outside of
/tmp, which then removes the need to mkstemp() them in the first
place, and they can just have consistent names.


We have created a set of unit file to start the Tizen Mobile UI. Since
this UI is not generically available, I'm not posting the unit file
for that just yet, but they will be available soon as part of the
tizen.org source code repositories.

An important note to me is that long-term I'd like to see most of the
functionality in user-session disappear in favor of code that natively
ships with systemd. This post is to kick off the discussion - It's a
quick and easy way for people to really start deploying `systemd
--user` sessions on real distro's without much effort, getting past
the point where they need to rely on xdg autostart or some other
method. The big problem to me is that there isn't much out there that
tries to be *minimal* and just does the pam conversation login and
setuid($USER), which is the core bit. User-session fills the gap.

I don't consider this code drop mergable in systemd, but, given it's
1000-line size and above summary, I think we could get there
relatively soon. We need to assess which bits can be easily removed -
but I've given plenty of hints to start off that discussion.

Last, I'd like to cheer - User sessions are here! They work! I don't
think it's going to be much work to iron out these remaining issues
and have even more of the system started up by systemd.


Auke


References:
user-session code: http://foo-projects.org/~sofar/user-session/
uxlaunch code: http://foo-projects.org/~sofar/uxlaunch/
See also http://tizen.org/ (code should post here soon, although dates
are not yet set)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: user-session-1.tar.gz
Type: application/x-gzip
Size: 103699 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20120412/ec5316c3/attachment-0001.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: dbus.socket
Type: application/octet-stream
Size: 237 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20120412/ec5316c3/attachment-0001.obj>


More information about the systemd-devel mailing list