Commit Graph
60 Commits
Author SHA1 Message Date
David Herrmann 154034270c logind: allow switching to unused VTs via SwitchTo()
If compositors use the new SwitchTo() logic to map F1-F12, we should allow
them to switch to unregistered VTs, too. Otherwise, the auto-spawn logic
of gettys won't trigger.

Reported-by: Jasper St. Pierre <jstpierre@mecheye.net>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2014-07-08 12:56:55 +02:00
David Herrmann 9489490a69 util: add realloc_multiply() helper
This is similar to malloc_multiply() and friends. It is realloc() with a
multiplication-overflow check.
2014-06-16 15:23:06 +02:00
David Herrmann 368504f485 util: fix multiply-alloc helpers with size==0
Passing 0 to malloc() is not required to return NULL. Therefore, don't
bail out if "b" is 0. This is not of importance to the existing helpers,
but the upcoming realloc_multiply() requires this. To keep consistence, we
keep the same behavior for the other helpers.
2014-06-16 15:22:57 +02:00
David Herrmann d442e2ec6e macro: add DISABLE_WARNING_SHADOW
As it turns out, we cannot use _Pragma in compound-statements. Therefore,
constructs like MIN(MAX(a, b), x) will warn due to shadowed variable
declarations. The DISABLE_WARNING_SHADOW macro can be used to suppress
these.

Note that using UNIQUE(_var) does not work either as GCC uses the last
line of a macro-expansion for __LINE__, therefore, still causing both
macros to have the same variables. We could use different variable-names
for MIN and MAX, but that just hides the problem and still fails for
MIN(something(MIN(a, b)), c).

The only working solution is to use __COUNTER__ and pass it pre-evaluated
as extra argument to a macro to use as name-prefix. This, however, makes
all these macros much more complicated so I'll go with manual
DISABLE_WARNING_SHADOW so far.
2014-06-16 15:22:46 +02:00
David Herrmann e0dd92729e shared: add ring buffer
New "struct ring" object that implements a basic ring buffer for arbitrary
byte-streams. A new basic runtime test is also added.

This will be needed for our pty helpers for systemd-console and friends.
2014-05-13 22:08:13 +02:00
David Herrmann 625e870b4f shared: add ALIGN_POWER2 macro
Sounds easy, turns out to be horrible to implement: ALIGN_POWER2 returns
the next higher power of 2. clz(0) is undefined, same is true for
left-shift-overflows, yey, C rocks!
2014-05-13 22:05:32 +02:00
David Herrmann 8b8fa8b80c login: rename session_mute_vt() to session_prepare_vt()
This function is no longer just about muteing the VT. We do all kinds of
VT setup for sessions using the controller-API. Rename the function to
something more appropriate.
2014-04-23 17:38:43 +02:00
David Herrmann 9541666b8d login: add 'mir' to the list of session types
Add Mir to the list of session types. This is implemented for LightDM
in lp:~robert-ancell/lightdm/xdg-session-desktop [1].

[1] https://code.launchpad.net/~robert-ancell/lightdm/xdg-session-desktop/+merge/214108

(david: adjusted commit-header and fixed whitespace issues)
2014-04-09 21:22:48 +02:00
David Herrmann 8016b90484 logind: don't print error if devices vanish during ACL-init
If a device is unplugged while we initialize it, we will get ENOENT for
ACL-init (and related stuff). We currently print errors then, which is
misleading. Print a debug-message early and continue.
2014-04-02 16:25:40 +02:00
David Herrmann 374c356979 sd-bus: mark sd_bus_unref() as broken regarding self-refs
If you allocate a message with bus==NULL and then unref the main bus,
it will free your message underneath and your program will go boom!

To fix that, we really need to figure out what the semantics for
self-references (m->bus) should be and when/where/what accesses are
actually allowed.

Same is true for the pseudo-thread-safety we employ..
2014-03-22 19:35:25 +01:00
David Herrmann eb33a6f858 sd-bus: add note about sd_bus_unref() recursion
In sd_bus_unref() we check for self-reference loops and destruct our
queues in case we're the only reference holders. However, we do _not_
modify our own ref-count, thus effectively causing the
message-destructions to enter with the same reference count as we did.

The only reason this doesn't cause an endless recursion (or trigger
assert(m->n_ref > 0) in sd_bus_message_unref()) is the fact that we
decrease queue-counters _before_ calling _unref(). That's not obvious at
all, so add a big fat note in bus_reset_queues() to everyone touching that
code.
2014-03-22 18:06:38 +01:00
David Herrmann 22fdeadcc0 sd-rtnl: fix self-reference leaks
Like sd-bus, sd-rtnl can have self-references through queued messages. In
particular, each queued message has the following self-ref loop:
  rtnl->wqueue[i]->rtnl == rtnl
Same is true for "rqueue".

When sd_rtnl_unref() gets called, we must therefore make sure we correctly
consider each self-reference when deciding to destroy the object. For each
queued message, there _might_ be one ref. However, rtnl-messages can be
created _without_ a bus-reference, therefore we need to verify the
actually required ref-count.

Once we know exactly how many self-refs exist, and we verified none of the
queued messages has external references, we can destruct the object.
We must immediately drop our own reference, then flush all queues and
destroy the bus object. Otherwise, each sd_rtnl_message_unref() call would
recurse into the same destruction logic as they enter with the same
rtnl-refcnt.

Note: We really should verify _all_ queued messages have m->rtnl set to
      the bus they're queued on. If that's given, we can change:
        if (REFCNT_GET(rtnl->n_ref) <= refs)
      to
        if (REFCNT_GET(rtnl->n_ref) == refs)
      and thus avoid recalculating the required refs for each message we
      remove from the queue during destruction.
2014-03-22 18:00:03 +01:00
David Herrmann 22e7062d74 NEWS: fix typo
sittings -> settings
2014-03-11 20:47:40 +01:00
David Herrmann 3e6b205f81 login: set pos-slot to fallback on pos-eviction
If we evict a session position, we open the position slot for future
sessions. However, there might already be another session on the same
position if both were started on the same VT. This is currently done if
gdm spawns the session on its own Xserver.

Hence, look for such a session on pos-eviction and claim the new slot
immediately.
2014-02-25 13:10:42 +01:00
David Herrmann a1937e679f login: fix pos-array allocation
GREEDY_REALLOC takes a pointer to the real size, not the array-width as
argument. Therefore, our array is currently way to small to keep the seat
positions.

Introduce GREEDY_REALLOC0_T() as typed version of GREEDY_REALLOC and store
the array-width instead of array-size.
2014-02-25 12:22:31 +01:00
David Herrmann d8148cc59d build-sys: remove libsystemd.sym duplicate
sd_memfd_new is available twice. Remove the second one.
2014-02-23 00:26:29 +01:00
David Herrmann 7a0d4a3d16 event: fix crash on child-source state modifications
Setting a child-source state to anything but SD_EVENT_OFF currently does
nothing. The condition logic is flawed. Move the state update *below* the
test for the previous state.

Fixes a crash if you call:
  sd_event_source_set_enabled(source, SD_EVENT_ON);
  sd_event_source_unref(source);
on a child-source in its own callback.
2014-02-19 13:24:38 +01:00
David Herrmann a020c2a013 sd-bus: export sd_bus_call{,_async,_async_cancel}
The .sym file somehow lacks these declarations, so add these. You have to
run "make clean" to make sure the sym-test runs fine afterwards.
2014-02-10 23:25:10 +01:00
David Herrmann 49e6fdbf14 logind: introduce session "positions"
logind has no concept of session ordering. Sessions have a unique name,
some attributes about the capabilities and that's already it. There is
currently no stable+total order on sessions. If we use the logind API to
switch between sessions, we are faced with an unordered list of sessions
we have no clue of.

This used to be no problem on seats with VTs or on seats with only a
single active session. However, with the introduction of multi-session
capability for seats without VTs, we need to find a way to order sessions
in a stable way.

This patch introduces session "positions". A position is a simple integer
assigned to a session which is never changed implicitly (currently, we
also don't change it explicitly, but that may be changed someday). For
seats with VTs, we force the position to be the same as the VTnr. Without
VTs, we simply find the lowest unassigned number and use it as position.
If position-assignment fails or if, for any reason, we decide to not
assign a position to a session, the position is set to 0 (which is treated
as invalid position).
During session_load() or if two sessions have the same VTnr, we may end up
with two sessions with the same position (this shouldn't happen, but lets
be fail-safe in case some other part of the stack fails). This case is
dealt with gracefully by ignoring any session but the first session
assigned to the position. Thus, session->pos is a hint, seat->positions[i]
is the definite position-assignment. Always verify both match in case you
need to modify them!

Additionally, we introduce SwitchTo(unsigned int) on the seat-dbus-API.
You can call it with any integer value != 0 and logind will try to switch
to the request position. If you implement a compositor or any other
session-controller, you simply watch for ctrl+alt+F1 to F12 and call
SwitchTo(Fx). logind will figure a way out deal with this number.
For convenience, we also introduce SwitchToNext/Previous(). It should be
called on ctrl+alt+Left/Right (like the kernel-console used to support).

Note that the public API (SwitchTo*()) is *not* bound to the underlying
logic that is implemented now. We don't export "session-positions" on the
dbus/C API! They are an implementation detail. Instead, the SwitchTo*()
API is supposed to be a hint to let logind choose the session-switching
logic. Any foreground session-controller is free to enumerate/order
existing sessions according to their needs and call Session.Activate()
manually. But the SwitchTo*() API provides a uniform behavior across
session-controllers.

Background: Session-switching keys depend on the active keymap. The XKB
specification provides the XKB_KEY_XF86Switch_VT_1-12 key-symbols which
have to be mapped by all keymaps to allow session-switching. It is usually
bound to ctrl+alt+Fx but may be set differently. A compositor passes any
keyboard input to XKB before passing it to clients. In case a key-press
invokes the XKB_KEY_XF86Switch_VT_x action, the keypress is *not*
forwarded to clients, but instead a session-switch is scheduled.

This actually prevents us from handling these keys outside of the session.
If an active compositor has a keymap with a different mapping of these
keys, and logind itself tries to catch these combinations, we end up with
the key-press sent to the compositor's clients *and* handled by logind.
This is *bad* and we must avoid this. The only situation where a
background process is allowed to handle key-presses is debugging and
emergency-keys. In these cases, we don't care for keymap mismatches and
accept the double-event. Another exception is unmapped keys like
PowerOff/Suspend (even though this one is controversial).
2014-01-20 22:30:01 +01:00
David Herrmann e10e429f2d TODO: extend login capability note
We cannot remove CAP_SYS_ADMIN, which basically makes removing all other
capabilities useless. Anyhow, still wouldn't hurt checking whether stuff
like CAP_KILL can be dropped from logind.
2014-01-05 13:59:12 +01:00
David Herrmann 559a4d4cda core: fix Unit.SetProperties argument parsing
SetProperties has signature "ba(sv)", but the bus_unit_set_properties()
helper already does a enter_container('a', "sv") so we have to skip it in
bus_unit_method_set_properties().
2013-12-10 23:28:07 +00:00
David Herrmann 4545a231fc util: add greedy_realloc0()
Compared to greedy_realloc(), this sets all newly allocated memory to 0.
As the old variant has been used a lot for string-handling, we avoid
changing it as clearing memory is not needed there.
2013-12-01 12:17:08 +01:00
David Herrmann a0a6be9f6a logind: fix "CanGraphical" attribute to return correct value
We should return seat_can_graphical() instead of seat_can_tty() for the
public dbus CanGraphical attribute. This used to work, but the
dbus -> sd-bus conversion introduced this regression.
2013-12-01 12:17:08 +01:00
David Herrmann 486cd82c8f logind: remove unused session->closing field
This field is always false, drop it. If you want a reliable way to get
session state, call session_get_state(). Testing for any flags directly
doesn't work currently so don't pretend it would.
2013-11-28 17:41:44 +01:00
David Herrmann c506027af8 logind: require VTs on seat0 and forbid elsewhere
Sessions on seat0 must pass us a vtnr, otherwise, you shouldn't try
attaching it to seat0. For seats without VTs, we do the exact opposite: we
forbid VTs.

There can be odd situations if the session-files contain invalid
combinations. However, we try to keep sessions alive and restore state as
good as possible.
2013-11-28 17:41:38 +01:00
David Herrmann 92bd5ff3a0 logind: make VT numbers unsigned
Fix the whole code to use "unsigned int" for vtnr. 0 is an invalid vtnr so
we don't need negative numbers at all.

Note that most code already assumes it's unsigned so in case there's a
negative vtnr, our code may, under special circumstances, silently break.
So this patch makes sure all sources of vtnrs verify the validity. Also
note that the dbus api already uses unsigned ints.
2013-11-28 17:38:16 +01:00
David Herrmann 2a16a986ce event: allow EPOLLET as event flag
EPOLLET enables edge-triggered mode (see epoll(7) for more). For most
use-cases, level-triggered is just fine, but for master-TTYs we need
edge-triggered to catch EPOLLHUP. master-TTYs signal EPOLLHUP if no client
is connected, but a client may connect some time later (same happens
during vhangup(2)).

However, epoll doesn't allow masking EPOLLHUP so it's signaled constantly.
To avoid this, edge-triggered mode is needed.
2013-11-28 15:16:49 +01:00
David Herrmann 90a18413f8 logind: mute/restore VT on behalf of session controllers
If a session process calls TakeControl(), we now put the VT into
KD_GRAPHICS+K_OFF mode. This way, the new session controller can solely
rely on the logind-dbus API to manage the session.

Once the controller exits or calls ReleaseControl(), we restore the VT. We
also restore it, if we lost a controller during crash/restart (but only if
there really *was* a controller previously).

Note that we also must put the VT into VT_PROCESS mode. We want VT_AUTO
semantics, but VT_AUTO+KD_GRAPHICS actually disables *all* VT switches
(who came up with that great idea?). Hence, we set VT_PROCESS for logind
but acknowledge *all* requests immediately.

If a compositor wants custom VT setups, they can still get this by *first*
calling TakeControl() and afterwards setting up the VT. logind doesn't
touch the VT during controller runtime, only during setup/teardown. This
is actually what weston already does.
2013-11-28 15:16:49 +01:00
David Herrmann 6d33772f9a logind: restore session-controller after crash
We now save the unique bus-name of a session-controller as CONTROLLER=%s
in the session files. This allows us to restore the controller after a
crash or restart.

Note that we test whether the name is still valid (dbus guarantees that
the name is unique as long as the machine is up and running). If it is,
we know that the controller still exists and can safely restore it. Our
dbus-name-tracking guarantees that we're notified once it exits.

Also note that session-devices are *not* restored. We have no way to know
which devices where used before the crash. We could store all these on
disk, too, or mark them via udev. However, this seems to be rather
cumbersome. Instead, we expect controllers to listen for NewSession
signals for their own session. This is sent on session_load() and they can
then re-request all devices.

The only race I could find is if logind crashes, then the session
controller tries calling ReleaseControl() (which will fail as logind is
down) but keeps the bus-connection valid for other independent requests.
If logind is restarted, it will restore the old controller and thus block
the session.
However, this seems unlikely for several reasons:
 - The ReleaseControl() call must occur exactly in the timespan where
   logind is dead.
 - A process which calls ReleaseControl() usually closes the
   bus-connection afterwards. Especially if ReleaseControl() fails, the
   process should notice that something is wrong and close the bus.
 - A process calling ReleaseControl() usually exits afterwards. There may
   be any cleanup pending, but other than that, usual compositors exit.
 - If a session-controller calls ReleaseControl(), a session is usually
   considered closing. There is no known use-case where we hand-over
   session-control in a single session. So we don't care whether the
   controller is locked afterwards.

So this seems negligible.
2013-11-28 15:16:49 +01:00
David Herrmann d1107170f9 logind: ignore failing close() on session-devices
Unfortunately, close() on a revoked/removed character-device fails with
ENODEV. I tried tracking this down in the kernel, but couldn't figure out
were exactly it comes from. However, can be easily reproduced with:
  fd = open("/dev/input/event0", O_RDWR);
  ioctl(fd, EVIOCREVOKE, 0);
  r = close(fd);
A second close on @fd would return EBADF so the close is actually valid.

We simply ignore close() errors for all session-devices as their access
may be revoked asynchronously, or the device might get unplugged.
We use close_nointr() in case anyone ever looks at the return value (or
anyone runs "grep 'close(' -r src/" to find broken close() calls).

Fixes:
  systemd-logind[31992]: Assertion 'close_nointr(fd) == 0' failed at src/shared/util.c:185, function close_nointr_nofail(). Aborting.
2013-11-28 15:16:49 +01:00
David Herrmann 5fd38859b3 bus: add bus_name_has_owner() helper
Small helper to run a synchronous "NameHasOwner" request on the
dbus-daemon.
2013-11-28 15:16:49 +01:00
David Herrmann 3fdb2494c1 login: revert lazy session-activation on non-VT seats
Existing applications like gdm already depend on new sessions to get
immediately activated on seats without VTs. Fixes a bug reported as:
  [systemd-devel] systemd 208:trouble with inactive user sessions at non-seat0 seats

This patch restores the original behavior. We either need to add a new
flag for session-creation or some other heuristic to avoid activating new
sessions in the future.
2013-11-28 10:52:18 +01:00
David Herrmann b5be11e823 macro: fix assert_cc() fallback
We need two-level macro-expansion, otherwise __LINE__ will not get
evaluated.
2013-11-22 09:19:00 +01:00
David Herrmann 0cc1125ae1 event: clear pending-state when re-arming timers
If a timer fires and is marked pending, but an application re-arms it
before it is dispatched, we now clear the pending state.

This fixes a bug where an application arms a timer, which fires and is
marked pending. But before it is dispatched, the application loses
interest in it and disables it. Now if the timer is re-armed and
re-enabled later, it will be immediately dispatched as it is still marked
pending.

This behavior is unexpected, so avoid it by clearing pending state when
re-arming timers. Note that applications have no way to clear pending
state themselves, so there's no current workaround.
2013-11-20 16:19:58 +01:00
David Herrmann 6c1508b871 bus: fix error-path in bus_map_all_properties()
We shouldn't return positive integers on errors. Fix the typo by removing
the negation.
2013-11-20 16:19:58 +01:00
David Herrmann e9718c12a4 rules: simply 60-drm.rules
We don't need any GOTO, if we merge all matches into a single line.
2013-11-03 16:17:14 +01:00
David Herrmann 0a8da8305a rules: load path_id on DRM devices
The path_id-builtin provides useful unique aliases for DRM devices. If we
want to configure DRM render-nodes for compositors, we want to avoid
storing the whole sys-path in configuration files. Hence, allow users to
store the short PATH_ID instead.

Load path_id-builtin unconditionally on DRM devices now to always provide
this alias.
2013-11-03 13:12:13 +01:00
David Herrmann d7d1c8f983 logind: allow unprivileged session-device access
The session-device/control API was introduced for unprivileged device
access from within a session. Add the required dbus policy to the default
logind policies.

Note: logind validates that only root and the user of a session can
use the API. Furthermore, only a single API user gets access at a time.
2013-10-15 21:13:39 +02:00
David Herrmann 0581dac2c1 login: make sd_session_get_vt() actually work
We use VTNR, not VTNr as key. Until now sd_session_get_vt() just returns
an error.
2013-10-10 13:14:18 +02:00
David Herrmann 3f4fee033b login: fix invalid free() in sd_session_get_vt()
We need to clear variables markes as _cleanup_free_. Otherwise, our
error-paths might corrupt random memory.
2013-10-10 13:14:18 +02:00
David Herrmann ef7939dfbb Update TODO
Remove "logind fbdev removal" as it is no longer supported.
2013-10-01 19:21:35 +02:00
David Herrmann 3a83f5223a logind: remove fbdev session-device support
fbdev does not support access-handover so it is quite useless to route it
through logind. If compositors want to use it they ought to open it
themselves. It's highly recommended to be ignored entirely, though. fbdev
is about to be deprecated in the kernel.
2013-10-01 18:08:54 +02:00
David Herrmann 11c2f7a813 logind: run with CAP_SYS_ADMIN
DRM Master access requires CAP_SYS_ADMIN, yay! Add it to the capability
bounding set for systemd-logind. As CAP_SYS_ADMIN actually allows a huge
set of actions, this mostly renders the restriction-set useless. Anyway,
patches are already pending to reduce the restriction on the kernel side.
But these won't really make it into any stable-release so for now we're
stuck with CAP_SYS_ADMIN.
2013-10-01 17:59:44 +02:00
David Herrmann dfd552707d logind: send PropertyChanged during deactivation
We only send the PropertyChanged signal for the to-be-activated session
but not for the to-be-deactivated one. Fix that so both listeners get
notified about the new state.
2013-10-01 17:58:58 +02:00
David Herrmann c2e5d024a3 logind: check whether first drmSetMaster succeeded
The initial drmSetMaster may fail if there is an active master already. We
must not assume that all existing clients comply to logind rules. We check
for this during session-activation already but didn't during device setup.
Fix this by checking the return code.

As drmSetMaster has had horrible return codes in the past (0 for failure?
EINVAL for denied access, ..) we need to be quite pedantic. To guarantee
an open file-descriptor we need to close the device and reopen it without
master if setting master failed first.
2013-10-01 17:53:43 +02:00
David Herrmann 081dfa852f logind: fix session-device dbus notify
Had this fix lying around here for some time. Thanks to missing
type-checking for va-args we passed in the actual major/minor values
instead of pointers to it. Fix it by saving the values on the stack first
and passing in the pointers.
2013-10-01 17:48:15 +02:00
David Herrmann 831dedef66 logind: fix build for ARM with sizeof(dev_t) > sizeof(void*)
Unfortunately on ARM-32 systems dev_t can be 64bit and thus we cannot
store it easily in void* keys for hashtables. Fix that by passing a
pointer to the dev_t variable instead.
2013-09-17 19:08:51 -04:00
David Herrmann d7bd01b547 logind: implement generic multi-session
This enables the multi-session capability for seats that don't have VTs.
For legacy seats with VTs, everything stays the same. However, all other
seats now also get the multi-session capability.

The only feature that was missing was session-switching. As logind can
force a session-switch and signal that via the "Active" property, we only
need a way to allow synchronized/delayed session switches. Compositors
need to cleanup some devices before acknowledging the session switch.
Therefore, we use the session-devices to give compositors a chance to
block a session-switch until they cleaned everything up.

If you activate a session on a seat without VTs, we send a PauseDevice
signal to the active session for every active device. Only once the
session acknowledged all these with a PauseDeviceComplete() call, we
perform the final session switch.

One important note is that delayed session-switching is meant for
backwards compatibility. New compositors or other sessions should really
try to deal correctly with forced session switches! They only need to
handle EACCES/EPERM from syscalls and treat them as "PauseDevice" signal.

Following logind patches will add a timeout to session-switches which
forces the switch if the active session does not react in a timely
fashion. Moreover, explicit ForceActivate() calls might also be supported.
Hence, sessions must not crash if their devices get paused.
2013-09-17 17:15:30 -05:00
David Herrmann 118ecf3242 logind: introduce session-devices
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 follow 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 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 major/minor exclusively to
identify devices on the bus. On RequestDevice() we retrieve the
udev_device from the major/minor 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 major/minor. 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 the
controller changes or a session is closed or removed.
2013-09-17 17:15:30 -05:00
David Herrmann bf7825ae69 logind: extract has_vts() from can_multi_session()
We currently use seat_can_multi_session() to test for two things:
 * whether the seat can handle session-switching
 * whether the seat has VTs

As both are currently logically equivalent, we didn't care. However, we
want to allow session-switching on seats without VTs, so split this helper
into:
 * seat_can_multi_session(): whether session-switching is supported
 * seat_has_vts(): whether the seat has VTs

Note that only one seat on a system can have VTs. There is only one set of
them. We automatically assign them to seat0 as usual.

With this patch in place, we can easily add new session-switching/tracking
methods without breaking any VT code as it is now protected by has_vts(),
no longer by can_multi_session().
2013-09-17 13:49:24 -05:00
David Herrmann 9209d5121d logind: fix session_activate(vtnr = 0)
VT numbers start with 1. If a session has vtnr == 0, we must not assume it
is running on a VT.
Note that this could trigger the assert() below as CreateSession() sets
vtnr to 0, not <0.
2013-09-17 13:48:41 -05:00
David Herrmann 20e1bd9d1b logind: fix seat_can_tty() to check for VTs
A seat provides text-logins if it has VTs. This is always limited to seat0
so the seat_is_seat0() check is correct. However, if VTs are disabled, no
seat provides text-logins so we also need to check for the console-fd.

This was previously:
  return seat_is_vtconsole();
It looked right, but was functionally equivalent to seat_is_seat0(). The
rename of this helper made it more obvious that it is missing the VT test.
2013-09-17 13:48:23 -05:00
David Herrmann 92432fcc7f logind: rename vtconsole to seat0
The seat->vtconsole member always points to the default seat seat0. Even
if VTs are disabled, it's used as default seat. Therefore, rename it to
seat0 to correctly state what it is.

This also changes the seat files in /run from IS_VTCONSOLE to IS_SEAT0. It
wasn't used by any code, yet, so this seems fine.

While we are at it, we also remove every "if (s->vtconsole)" as this
pointer is always valid!
2013-09-17 13:47:19 -05:00
David Herrmann ab60f2ffb1 logind: make Session.Activate() lazy
Currently, Activate() calls chvt(), which does an ioctl(VT_ACTIVATE) and
immediately calls seat_set_active(). However, VTs are allowed to prevent
being deactivated. Therefore, logind cannot be sure the VT_ACTIVATE call
was actually successful.

Furthermore, compositors often need to clean up their devices before they
acknowledge the VT switch. The immediate call to seat_set_active() may
modify underlying ACLs, though. Thus, some compositors may fail cleaning
up their stuff. Moreover, the compositor being switched to (if listening
to logind instead of VTs) will not be able to activate its devices if the
old VT still has them active.

We could simply add an VT_WAITACTIVE call, which blocks until the given VT
is active. However, this can block forever if the compositor hangs.

So to fix this, we make Activate() lazy. That is, it only schedules a
session-switch but does not wait for it to complete. The caller can no
longer rely on it being immediate. Instead, a caller is required to wait
for the PropertiesChanged signal and read the "Active" field.

We could make Activate() wait asynchronously for the session-switch to
complete and then send the return-message afterwards. However, this would
add a lot of state-tracking with no real gain:
 1) Sessions normally don't care whether Activate() was actually
    successful as they currently _must_ wait for the VT activation to do
    anything for real.
 2) Error messages for failed session switches can be printed by logind
    instead of the session issuing Activate().
 3) Sessions that require synchronous Activate() calls can simply issue
    the call and then wait for "Active" properties to change. This also
    allows them to implement their own timeout.

This change prepares for multi-session on seats without VTs. Forced VT
switches are always bad as compositors cannot perform any cleanup. This
isn't strictly required, but may lead to loss of information and ambiguous
error messages.
So for multi-session on seats without VTs, we must wait for the current
session to clean-up before finalizing the session-switch. This requires
Activate() to be lazy as we cannot block here.

Note that we can always implement a timeout which allows us to guarantee
the session switch to happen. Nevertheless, this calls for a lazy
Activate().
2013-09-17 11:33:18 -05:00
David Herrmann ae5e06bda2 logind: add session controllers
A session usually has only a single compositor or other application that
controls graphics and input devices on it. To avoid multiple applications
from hijacking each other's devices or even using the devices in parallel,
we add session controllers.

A session controller is an application that manages a session. Specific
API calls may be limited to controllers to avoid others from getting
unprivileged access to restricted resources. A session becomes a
controller by calling the RequestControl() dbus API call. It can drop it
via ReleaseControl().

logind tracks bus-names to release the controller once an application
closes the bus. We use the new bus-name tracking to do that. Note that
during ReleaseControl() we need to check whether some other session also
tracks the name before we remove it from the bus-name tracking list.

Currently, we only allow one controller at a time. However, the public API
does not enforce this restriction. So if it makes sense, we can allow
multiple controllers in parallel later. Or we can add a "scope" parameter,
which allows a different controller for graphics-devices, sound-devices
and whatever you want.
Note that currently you get -EBUSY if there is already a controller. You
can force the RequestControl() call (root-only) to drop the current
controller and recover the session during an emergency. To recover a seat,
this is not needed, though. You can simply create a new session or
force-activate it.

To become a session controller, a dbus caller must either be root or the
same user as the user of the session. This allows us to run a session
compositor as user and we no longer need any CAP_SYS_ADMIN.
2013-09-17 11:33:18 -05:00
David Herrmann e8b212fe56 logind: add infrastructure to watch busnames
If we want to track bus-names to allow exclusive resource-access, we need
a way to get notified when a bus-name is gone. We make logind watch for
NameOwnerChanged dbus events and check whether the name is currently
watched. If it is, we remove it from the watch-list (notification for
other objects can be added in follow-up patches).
2013-09-17 11:30:44 -05:00
David Herrmann 718d006a63 logind: listen actively for session devices
Session compositors need access to fbdev, DRM and evdev devices if they
control a session. To make logind pass them to sessions, we need to
listen for them actively.

However, we avoid creating new seats for non master-of-seat devices. Only
once a seat is created, we start remembering all other session devices. If
the last master-device is removed (even if there are other non-master
devices still available), we destroy the seat. This is the current
behavior, but we need to explicitly implement it now as there may be
non-master devices in the seat->devices list.

Unlike master devices, we don't care whether our list of non-master
devices is complete. We don't export this list but use it only as cache if
sessions request these devices. Hence, if a session requests a device that
is not in the list, we will simply look it up. However, once a session
requested a device, we must be notified of "remove" udev events. So we
must link the devices somehow into the device-list.

Regarding the implementation, we now sort the device list by the "master"
flag. This guarantees that master devices are at the front and non-master
devices at the tail of the list. Thus, we can easily test whether a seat
has a master device attached.
2013-09-17 11:15:29 -05:00
David Herrmann d8c8508bad build: check for build/link flags harder
Use AC_LINK_IFELSE instead of AC_COMPILE_IFELSE to test for flags that
might succeed during compilation but not during linking. An example is gcc
compiled with libssp support but gnu-ld without it. In this case
-fstack-protector works fine during compilation but fails during linking
as several internal helpers are missing.
2013-09-16 09:59:16 -05:00
David Herrmann f9e84da678 libudev: enumerate: fix NULL-deref for subsystem-matches
udev_device_get_subsystem() may return NULL if no subsystem could be
figured out by libudev. This might be due to OOM or if the device
disconnected between the udev_device_new() call and
udev_device_get_subsystem(). Therefore, we need to handle subsystem==NULL
safely.

Instead of testing for it in each helper, we treat subsystem==NULL as
empty subsystem in match_subsystem().

Backtrace of udev_enumerate with an input-device disconnecting in exactly
this time-frame:
 (gdb) bt
 #0  0x00007ffff569dc24 in strnlen () from /usr/lib/libc.so.6
 #1  0x00007ffff56d9e04 in fnmatch@@GLIBC_2.2.5 () from /usr/lib/libc.so.6
 #2  0x00007ffff5beb83d in match_subsystem (udev_enumerate=0x7a05f0, subsystem=0x0) at src/libudev/libudev-enumerate.c:727
 #3  0x00007ffff5bebb30 in parent_add_child (enumerate=enumerate@entry=0x7a05f0, path=<optimized out>) at src/libudev/libudev-enumerate.c:834
 #4  0x00007ffff5bebc3f in parent_crawl_children (enumerate=enumerate@entry=0x7a05f0, path=0x7a56b0 "/sys/devices/<shortened>/input/input97", maxdepth=maxdepth@entry=254) at src/libudev/libudev-enumerate.c:866
 #5  0x00007ffff5bebc54 in parent_crawl_children (enumerate=enumerate@entry=0x7a05f0, path=0x79e8c0 "/sys/devices/<shortened>/input", maxdepth=maxdepth@entry=255) at src/libudev/libudev-enumerate.c:868
 #6  0x00007ffff5bebc54 in parent_crawl_children (enumerate=enumerate@entry=0x7a05f0, path=path@entry=0x753190 "/sys/devices/<shortened>", maxdepth=maxdepth@entry=256) at src/libudev/libudev-enumerate.c:868
 #7  0x00007ffff5bec7df in scan_devices_children (enumerate=0x7a05f0) at src/libudev/libudev-enumerate.c:882
 #8  udev_enumerate_scan_devices (udev_enumerate=udev_enumerate@entry=0x7a05f0) at src/libudev/libudev-enumerate.c:919
 #9  0x00007ffff5df8777 in <random_caller> () at some/file.c:181
2013-09-04 17:58:56 +02:00
David Herrmann 51cc07576e libudev: fix memleak when enumerating childs
We need to free udev-devices again if they don't match. Funny that no-one
noticed it yet since valgrind is quite verbose about it.
Fix it and free non-matching devices.
2013-09-04 14:56:06 +02:00