[PATCH 00/10] Device Management for systemd-logind

Hi 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 described at: http://www.freedesktop.org/wiki/Software/systemd/logind/ The reason for this series and the basic idea is discussed on: http://dvdhrm.wordpress.com/2013/08/25/sane-session-switching/ If someone is not familiar with session-management and, more importantly, session-switching, I summarized it at: http://dvdhrm.wordpress.com/2013/08/24/session-management-on-linux/ http://dvdhrm.wordpress.com/2013/08/24/how-vt-switching-works/ 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. Regards David 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. Remarks: - 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 it. As long as a process owns a device, it is notified via dbus signals about state changes: 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 state. 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 usual. 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 fail. 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: RequestControl(bool force): 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! DropControl(): 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 -- 1.8.3.4