[systemd-devel] [PATCH 00/10] Device Management for systemd-logind
dh.herrmann at gmail.com
Sun Aug 25 05:46:05 PDT 2013
This series implements device management for logind. A session can now request
device access directly via logind dbus APIs. It extends the
org.freedesktop.login1.Session interface. The already existing interface is
The reason for this series and the basic idea is discussed on:
If someone is not familiar with session-management and, more importantly,
session-switching, I summarized it at:
Ok, feel free to dismiss the rest of this mail and look directly at the patches.
The commit-messages explain everything in detail. For all who are not familiar
with the systemd code-base, a summary can be found below.
Feedback welcome! I am open for any suggestions.
Tests, examples and more will be available at https://github.com/dvdhrm/libnovt
once the API stabilized.
CC: wayland-devel at fdo for weston-launch development.
The following calls are introduced by this series. They are added to the logind
dbus API for sessions. Hence, each call implicitly operates on a given session
and thus does not have to be passed as argument.
These methods are added:
RequestDevice(const char *node, int *fd_out, bool *paused_out):
logind tries to open a device node in /dev/ (@node) for the caller. It
checks that it is assigned to the seat of the caller, has the uaccess
flags set and then opens the fd and passes it back via @fd_out. The
@paused_out field will notify the caller whether the device is currently
paused or active.
- you cannot open a node twice in parallel
- you must use the canonical device-node (the one from devtmpfs) and
returned by udev_device_get_devnode(). No other nodes can be
supported; clients could trigger OOM otherwise
- logind keeps a copy of @fd internally to revoke access once the
session becomes inactive
ReleaseDevice(const char *node):
logind closes the device that was previously requested via
RequestDevice(). It revokes access from the fd and closes it internally.
The caller still has their own fd, but it will be useless. But they
obviously need to close() it theirself.
You may call RequestDevice() on the same node again, once you released
As long as a process owns a device, it is notified via dbus signals about state
PauseDevice(const char *node, const char *type):
logind sends this event whenever the device is paused or released. @node
contains the device-node passed to RequestDevice() and is unique. @type
is "gone" if the device was closed or removed from the seat. A
compositor normally gets a udev "remove" event at the same time. They
should treat it similarly.
@type is "force" if the device was forcibly paused by logind. A
compositor normally gets EACCES or EPERM simultaneously from any
syscalls it tries on the fd. Hence, it is adviced to handle EACCES/EPERM
the same as a PauseDevice("force") signal from systemd.
Last but not least, there is "pause" as @type. This is a kind request by
logind to the compositor to pause the device. A compositor gets a short
timeout to react to this event, cleanup everything and acknowledge the
signal via a call to PauseDeviceComplete(). If it doesn't react in a
timely manner, a PauseDevice("force") event will be sent.
ResumeDevice(const char *node, int *fd_out):
For every device requested via RequestDevice(), logind sends this event
whenever the device is resumed. It also puts a new file-descriptor into
@fd_out. A compositor is advised to close its old fd and use the new
one. For some device-types (namely evdev) it *must* use the new fd, as
the old one is revoked. For other device-types (namely DRM) both will
actually be the same as access can be restored.
Compositors are allowed to rely on this behavior for DRM. That is, if
it's a DRM device, they can close the new fd and keep the old one
(they're actually just dup()'ed). This allows them to retain their DRM
Methods to acknowledge some signals are:
PauseDeviceComplete(const char *node):
As mentioned above in PauseDevice, this is a method that can be called
by compositors to react to a PauseDevice("pause") request. After it is
called, the given device will be paused.
Note that device-management is available on all sessions regardless their type
and state. Moreover, device-state follows session-state but might also be paused
for other reasons that are currently not defined. This means, whenever a session
is inactive, all devices on this session are inactive, too. However, when a
session is active, a device might stay inactive (for instance if reactivation
failed). But logind guarantees that a device can never be used by anyone else
than the foreground session. Compositors can rely on that for security reasons.
logind itselfs takes care of revoking device access for inactive sessions
(synchronized with session-switches!). It also tries to resume every device
when a session is activated. But session-devices must not be used to watch
session state! A compositor has to use the PropertiesChanged() signal plus the
"Active" property of sessions for that! Session-devices do not replace this! On
sessions with VTs, this is obviously replaced by the VT_SETMODE interface as
Now additionally to the interface mentioned above, this series introduces
session-controllers. These try to prevent multiple compositors from running in
parallel. That is, when a compositor starts up, it calls RequestControl() on the
session in question. If there is already another compositor running, this will
The new device-management functions are limited to the active controller. No
other functions make use of controllers.
Note that the RequestControl() call might get a "scope" argument. So you can
have a controller with scope "graphics" and one for scope "sound", for example.
On each scope only a single controller is allowed, but the scopes don't
interfere. So logind makes sure that RequestDevice() on a graphics or input
device requires the "graphics" scope.
But now the API:
For now this misses a "scope" argument, so it currently implies
"graphics" scope. This function will make the caller the new controller
of the given session. logind watches the system bus and automatically
drops the controller once it disconnects.
If there is already a controller for the given scope, this returns
EBUSY. If @force is true, the active controller will be dropped and the
caller will get the new controller.
This function is restricted to callers with the same UID as the "User"
of the given session. If @force is true, the caller must be root.
So this call in combination with RequestDevice() allows us to run a
compositor in a session as normal user. Yay!
Drop control again. If the caller is not the current controller, this
does nothing. Note that this call is optional. logind watches the bus
for disconnect events and invokes this implicitly if a controller exits.
David Herrmann (10):
logind: listen actively for session devices
logind: add infrastructure to watch busnames
logind: add session controllers
logind: make Session.Activate() lazy
logind: introduce session-devices
logind: rename vtconsole to seat0
logind: fix seat_can_tty() to check for VTs
logind: fix session_activate(vtnr = 0)
logind: extract has_vts() from can_multi_session()
logind: implement generic multi-session
Makefile.am | 2 +
src/login/logind-dbus.c | 35 ++-
src/login/logind-device.c | 39 ++-
src/login/logind-device.h | 5 +-
src/login/logind-seat.c | 73 ++++--
src/login/logind-seat.h | 6 +-
src/login/logind-session-dbus.c | 156 ++++++++++++
src/login/logind-session-device.c | 517 ++++++++++++++++++++++++++++++++++++++
src/login/logind-session-device.h | 58 +++++
src/login/logind-session.c | 112 ++++++++-
src/login/logind-session.h | 8 +
src/login/logind.c | 151 +++++++++--
src/login/logind.h | 12 +-
13 files changed, 1109 insertions(+), 65 deletions(-)
create mode 100644 src/login/logind-session-device.c
create mode 100644 src/login/logind-session-device.h
More information about the systemd-devel