Hello,
Following the recent discussions on multiuser support in Tizen 3.0, I
dug further on the steps involved in a user session creation.
NB: I used the latest IVI release 20131015.1 for investigation.
Actual situation
================
The default Systemd target is the graphical.target. In particular, this
target will launch the following services in this order:
* ac.service (AMD daemon)
* launchpad-preload at app.service (launchpad for binary apps: OSP Apps,
Core Apps and even usual apps like weston-terminal)
* wrt_launchpad_daemon at app.service (launchpad for web apps)
* user-session at 5000.service: opens the 'app' user session (starts
systemd --user)
So before the user-session start, we have AMD and its launchpads running
as root (despite the name 'xxx at app.service' for launchpads).
The user-session at .service is particular, as it contains:
- User=%I (i.e. User=app)
- PAMName=login
- ExecStart=-/usr/lib/systemd/systemd --user
- Environment=.... (static env variables)
The PAMName=login setting makes that systemd will open a PAM session
with the pam service 'login' before starting systemd --user as 'app'.
Respectively, when the user session terminates, the PAM session is
closed too.
In the actual pam configuration, opening a 'login' session will call the
pam_systemd module (included in systemd). This modules sends a DBus
message to systemd login manager (systemd-logind) and gets some
information in the response. This information is used to initialize some
important environment variables. When the session terminates, the
pam_systemd module also tells systemd-logind that the user session ended
(and depending on its configuration, systemd-logind could kill any
remaining user processes).
The following diagram tries to show what happens when systemd starts the
user-session (bold=run as root, other run as user)
---------------------------- *systemd-logind*
--------*systemd* |
| | ^
| | |
| v |(un)register
| *PAM session* |
| *pam_systemd*-<-(dbus msg)--
| |
| |
| v
| systemd --user -------------------
| |
| |
| -> ... services inside user session
|
|
|
---> *amd & launchpads*
------------------------------------------------------
NB on IVI: actually, weston is started inside the user session using
weston-launch. IMO this is not the right way to do things as
weston-launch (setuid root) clears the actual environment, opens a new
PAM session then forks a login shell which exec() weston (!). So there's
a second PAM session opened but moreover, all the environment variables
propagated from the top are erased: that's why we have to put
workarounds for environment vars everywhere (in service files, in
/etc/sysconfig, in /etc/profile.d and probably in some other places)...
Proposition for the future
==========================
As described previously on this list, we'll probably agree on having a
global AMD daemon with its launchpads, running as root (or at least as a
privileged user). The consequence is that we have to push information to
AMD concerning the user sessions and appropriate environment within each
session.
From what I've seen in pam_systemd code (see here:
https://review.tizen.org/gerrit/gitweb?p=platform/upstream/systemd.git;a=blob;f=src/login/pam-module.c;h=13290fd8ea6de3fcbb621e99dc7d92e7be50a030;hb=HEAD),
we have the following vars initialized before running systemd --user:
- coming from pam_env.so (/etc/environment)
* nothing (yet)
- coming from systemd-logind:
* XDG_SESSION_ID
* XDG_RUNTIME_DIR
* XDG_SEAT
* XDG_VTNR
- coming from user-session at .service (static vars in service):
* DISPLAY
* XDG_RUNTIME_DIR (useless...)
* DBUS_SESSION_BUS_ADDRESS
- other usual variables set at login time:
* HOME
* USER
* LOGNAME
* LANG
* PATH
So, to initialize a user-session environment in AMD, we could have a
pam_amd module started *after* pam_systemd and responsible for sending
the "init" request to AMD with the current environment. AMD would record
the appropriate environment for the given user/session (uid, sid). This
env would then be pushed to launchpads every time an application must be
started.
Later during the user session, when the environment is complete (ex:
desktop is started and we have WAYLAND_DISPLAY set), we could then
update the environment in AMD, with some update policies: for example,
an environment variable that was already set at init() time can't be
updated by update() (we keep the initial value).
We'd get something like this:
---------------------------- *systemd-logind*
----------*systemd* |
| | ^
| | |
| v |(un)register
| *PAM session* |
| *pam_systemd*-<-(dbus msg)--
| ---*pam_amd*
| | |
| | |
| | v
| | systemd --user -------------------
| | |
| | |
| | -> ...... services inside user session
...........
| | | | ^
| | | | |
| |init(uid,sid,[env]) |update(uid,sid,[env]) |launch(app) |
| | | | |
| V v V |
-> *amd* -----------------------------------------------+------ |
| | |
| | apply env|
| launch(app,uid,[env])| & launch app|
| v |
------>
*launchpads*---------------------------------------------------------
Remark 1: Wether we have one environment per user or one environment per
session is actually discussed... Given the previous diagram, do we know
that a launch request comes from a given session ? It seems that we can
handle both cases by adding the origin session id to the launch request.
But I'm a bit wary about handling uid+session id actually because of
extra error handling and added complexity. Do we have use cases for this ?
Remark 2: I didn't draw user session termination. Of course, it's the
opposite: pam_amd would send an exit(uid,sid) to AMD to block any future
launch for this session.
Discussion
==========
What do you think of this "pam_amd" module ?
Even later when some new mechanisms will be used to open a user session,
I'm quite sure we'll still have a PAM session: this is just a standard
behaviour on any linux distro. So using a PAM lib to propagate the
session environment to AMD makes sense and would certainly allow a
smoother transition to anything else in the future.
Hope you love ASCII art ;)
--
Stéphane Desneux
Intel OTC - Vannes/FR