[systemd-devel] [PATCH v2 05/10] logind: introduce session-devices

2013-09-17 Thread David Herrmann
A session-device is a device that is bound to a seat and used by a
session-controller to run the session. This currently includes DRM, fbdev
and evdev devices. A session-device can be created via RequestDevice() on
the dbus API of the session. You can drop it via ReleaseDevice() again.
Once the session is destroyed or you drop control of the session, all
session-devices are automatically destroyed.

Session devices mimic the session active state. A device can be
active/running or inactive/paused. Whenever a session is not the active
session, no session-device of it can be active. That is, if a session is
not in foreground, all session-devices are paused.
Whenever a session becomes active, all devices are resumed/activated by
logind. If it fails, a device may stay paused.

With every session-device you request, you also get a file-descriptor
back. logind keeps a copy of this fd and uses kernel specific calls to
pause/resume the file-descriptors. For example, a DRM fd is muted
by logind as long as a given session is not active. Hence, the fd of the
application is also muted. Once the session gets active, logind unmutes
the fd and the application will get DRM access again.
This, however, requires kernel support. DRM devices provide DRM-Master for
synchronization, evdev devices have EVIOCREVOKE (pending on
linux-input-ML). fbdev devices do not provide such synchronization methods
(and never will).
Note that for evdev devices, we call EVIOCREVOKE once a session gets
inactive. However, this cannot be undone (the fd is still valid but mostly
unusable). So we reopen a new fd once the session is activated and send it
together with the ResumeDevice() signal.

With this infrastructure in place, compositors can now run without
CAP_SYS_ADMIN (that is, without being root). They use RequestControl() to
acquire a session and listen for devices via udev_monitor. For every
device-node they want to open, they call RequestDevice() on logind. This
returns a fd which they can use now. They no longer have to open the
devices themselves or call any privileged ioctls. This is all done by
logind.
Session-switches are still bound to VTs. Hence, compositors will get
notified via the usual VT mechanisms and can cleanup their state. Once the
VT switch is acknowledged as usual, logind will get notified via sysfs and
pause the old-session's devices and resume the devices of the new session.

To allow using this infrastructure with systems without VTs, we provide
notification signals. logind sends PauseDevice(force) dbus signals to
the current session controller for every device that it pauses. And it
sends ResumeDevice signals for every device that it resumes. For
seats with VTs this is sent _after_ the VT switch is acknowledged. Because
the compositor already acknowledged that it cleaned-up all devices.
However, for seats without VTs, this is used to notify the active
compositor that the session is about to be deactivated. That is, logind
sends PauseDevice(force) for each active device and then performs the
session-switch. The session-switch changes the Active property of the
session which can be monitored by the compositor. The new session is
activated and the ResumeDevice events are sent.

For seats without VTs, this is a forced session-switch. As this is not
backwards-compatible (xserver actually crashes, weston drops the related
devices, ..) we also provide an acknowledged session-switch. Note that
this is never used for sessions with VTs. You use the acknowledged
VT-switch on these seats.

An acknowledged session switch sends PauseDevice(pause) instead of
PauseDevice(force) to the active session. It schedules a short timeout
and waits for the session to acknowledge each of them with
PauseDeviceComplete(). Once all are acknowledged, or the session ran out
of time, a PauseDevice(force) is sent for all remaining active devices
and the session switch is performed.
Note that this is only partially implemented, yet, as we don't allow
multi-session without VTs, yet. A follow up commit will hook it up and
implemented the acknowledgements+timeout.

The implementation is quite simple. We use device-nodes exclusively to
identify devices on the bus. On RequestDevice() we retrieve the
udev_device from the device-node and search for an existing Device
object. If no exists, we create it. This guarantees us that we are
notified whenever the device changes seats or is removed.

We create a new SessionDevice object and link it to the related Session
and Device. Session-devices is a hashtable to lookup SessionDevice
objects via device-node. Device-session_devices is a linked list so we
can release all linked session-devices once a device vanishes.

Now we only have to hook this up in seat_set_active() so we correctly
change device states during session-switches. As mentioned earlier, these
are forced state-changes as VTs are currently used exclusively for
multi-session implementations.

Everything else are hooks to release all session-devices once 

Re: [systemd-devel] [PATCH v2 05/10] logind: introduce session-devices

2013-09-17 Thread Lennart Poettering
On Tue, 17.09.13 17:39, David Herrmann (dh.herrm...@gmail.com) wrote:

 arg name=\force\ type=\b\/\n \
/method\n \
method name=\DropControl\/\n\
 +  method name=\RequestDevice\\n   \
 +   arg name=\node\ type=\s\ direction=\in\/\n \
 +   arg name=\fd\ type=\h\ direction=\out\/\n  \
 +   arg name=\paused\ type=\b\ direction=\out\/\n  \
 +  /method\n \
 +  method name=\ReleaseDevice\\n   \
 +   arg name=\node\ type=\s\/\n  \
 +  /method\n
  \

Please rename this pair to TakeDevice() and ReleaseDevice().

 index 000..659f161
 --- /dev/null
 +++ b/src/login/logind-session-device.c
 @@ -0,0 +1,489 @@
 +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 +
 +/***
 +  This file is part of systemd.
 +
 +  Copyright 2013 David Herrmann
 +
 +  systemd is free software; you can redistribute it and/or modify it
 +  under the terms of the GNU Lesser General Public License as published by
 +  the Free Software Foundation; either version 2.1 of the License, or
 +  (at your option) any later version.
 +
 +  systemd is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 +  Lesser General Public License for more details.
 +
 +  You should have received a copy of the GNU Lesser General Public License
 +  along with systemd; If not, see http://www.gnu.org/licenses/.
 +***/
 +
 +#include assert.h
 +#include fcntl.h
 +#include libudev.h
 +#include linux/input.h
 +#include linux/ioctl.h
 +#include string.h
 +#include sys/ioctl.h
 +#include sys/stat.h
 +#include sys/types.h
 +#include unistd.h
 +
 +#include dbus-common.h
 +#include logind-session-device.h
 +#include util.h
 +
 +enum SessionDeviceNotifications {
 +SESSION_DEVICE_RESUME,
 +SESSION_DEVICE_TRY_PAUSE,
 +SESSION_DEVICE_PAUSE,
 +SESSION_DEVICE_RELEASE,
 +};
 +
 +static void session_device_notify(SessionDevice *sd, enum 
 SessionDeviceNotifications type) {
 +_cleanup_dbus_message_unref_ DBusMessage *m = NULL;
 +_cleanup_free_ char *path = NULL;
 +const char *t = NULL;
 +
 +assert(sd);
 +
 +if (!sd-session-controller)
 +return;
 +
 +path = session_bus_path(sd-session);
 +if (!path)
 +return;
 +
 +m = dbus_message_new_signal(path,
 +org.freedesktop.login1.Session,
 +(type == SESSION_DEVICE_RESUME) ? 
 ResumeDevice : PauseDevice);
 +if (!m)
 +return;
 +
 +if (!dbus_message_set_destination(m, sd-session-controller))
 +return;
 +
 +switch (type) {
 +case SESSION_DEVICE_RESUME:
 +if (!dbus_message_append_args(m,
 +  DBUS_TYPE_STRING, sd-node,
 +  DBUS_TYPE_UNIX_FD, sd-fd,
 +  DBUS_TYPE_INVALID))
 +return;
 +break;
 +case SESSION_DEVICE_TRY_PAUSE:
 +t = pause;
 +break;
 +case SESSION_DEVICE_PAUSE:
 +t = force;
 +break;
 +case SESSION_DEVICE_RELEASE:
 +t = gone;
 +break;
 +default:
 +return;
 +}
 +
 +if (t  !dbus_message_append_args(m,
 +   DBUS_TYPE_STRING, sd-node,
 +   DBUS_TYPE_STRING, t,
 +   DBUS_TYPE_INVALID))
 +return;
 +
 +dbus_connection_send(sd-session-manager-bus, m, NULL);
 +}
 +
 +static int sd_eviocrevoke(int fd)
 +{

{ please on the same line as the function name.

 +static bool warned;
 +int r;
 +
 +#ifndef EVIOCREVOKE
 +#  define EVIOCREVOKE _IOW('E', 0x91, int)
 +#endif

Please move this to missing.h.

 +
 +assert(fd = 0);
 +
 +r = ioctl(fd, EVIOCREVOKE, 1);
 +if (r  0) {
 +r = -errno;
 +if (r == -ENOSYS  !warned) {
 +warned = true;
 +log_warning(kernel does not support 
 evdev-revocation);
 +}
 +}
 +
 +return 0;
 +}
 +
 +static int sd_drmsetmaster(int fd)
 +{

{...

 +int r;
 +
 +#ifndef DRM_IOCTL_SET_MASTER
 +#  define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
 +#endif

→ missing.h!

 +
 +assert(fd = 0);
 +
 +r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
 +if