[systemd-commits] 5 commits - src/libsystemd-bus src/login
David Herrmann
dvdhrm at kemper.freedesktop.org
Thu Nov 28 06:26:30 PST 2013
src/libsystemd-bus/bus-util.c | 26 ++++++
src/libsystemd-bus/bus-util.h | 2
src/libsystemd-bus/sd-event.c | 4
src/login/logind-session-device.c | 6 -
src/login/logind-session.c | 162 ++++++++++++++++++++++++++++++++++----
src/login/logind-session.h | 7 +
6 files changed, 187 insertions(+), 20 deletions(-)
New commits:
commit 2a16a986ce5f1bdb7e96abfe14fcb9f34c9364b6
Author: David Herrmann <dh.herrmann at gmail.com>
Date: Wed Nov 27 10:36:35 2013 +0100
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.
diff --git a/src/libsystemd-bus/sd-event.c b/src/libsystemd-bus/sd-event.c
index 6a6581b..b5ddf71 100644
--- a/src/libsystemd-bus/sd-event.c
+++ b/src/libsystemd-bus/sd-event.c
@@ -584,7 +584,7 @@ _public_ int sd_event_add_io(
assert_return(e, -EINVAL);
assert_return(fd >= 0, -EINVAL);
- assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP)), -EINVAL);
+ assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
@@ -1022,7 +1022,7 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events)
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP)), -EINVAL);
+ assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(s->event), -ECHILD);
commit 90a18413f8be577a649900eca977e060273f2b5b
Author: David Herrmann <dh.herrmann at gmail.com>
Date: Thu Nov 28 15:10:24 2013 +0100
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.
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 0e1c40b..c12683c 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -20,9 +20,13 @@
***/
#include <errno.h>
+#include <fcntl.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <signal.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <unistd.h>
-#include <fcntl.h>
#include "sd-id128.h"
#include "sd-messages.h"
@@ -86,6 +90,7 @@ Session* session_new(Manager *m, const char *id) {
s->manager = m;
s->fifo_fd = -1;
+ s->vtfd = -1;
return s;
}
@@ -395,6 +400,8 @@ int session_load(Session *s) {
if (controller) {
if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
session_set_controller(s, controller, false);
+ else
+ session_restore_vt(s);
}
return r;
@@ -971,6 +978,101 @@ int session_kill(Session *s, KillWho who, int signo) {
return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
}
+static int session_open_vt(Session *s) {
+ char path[128];
+
+ if (s->vtnr <= 0)
+ return -1;
+
+ if (s->vtfd >= 0)
+ return s->vtfd;
+
+ sprintf(path, "/dev/tty%d", s->vtnr);
+ s->vtfd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+ if (s->vtfd < 0) {
+ log_error("cannot open VT %s of session %s: %m", path, s->id);
+ return -1;
+ }
+
+ return s->vtfd;
+}
+
+static int session_vt_fn(sd_event_source *source, const struct signalfd_siginfo *si, void *data) {
+ Session *s = data;
+
+ if (s->vtfd >= 0)
+ ioctl(s->vtfd, VT_RELDISP, 1);
+
+ return 0;
+}
+
+void session_mute_vt(Session *s) {
+ int vt, r;
+ struct vt_mode mode = { 0 };
+ sigset_t mask;
+
+ vt = session_open_vt(s);
+ if (vt < 0)
+ return;
+
+ r = ioctl(vt, KDSKBMODE, K_OFF);
+ if (r < 0)
+ goto error;
+
+ r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
+ if (r < 0)
+ goto error;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ r = sd_event_add_signal(s->manager->event, SIGUSR1, session_vt_fn, s, &s->vt_source);
+ if (r < 0)
+ goto error;
+
+ /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
+ * So we need a dummy handler here which just acknowledges *all* VT
+ * switch requests. */
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR1;
+ r = ioctl(vt, VT_SETMODE, &mode);
+ if (r < 0)
+ goto error;
+
+ return;
+
+error:
+ log_error("cannot mute VT %d for session %s (%d/%d)", s->vtnr, s->id, r, errno);
+ session_restore_vt(s);
+}
+
+void session_restore_vt(Session *s) {
+ _cleanup_free_ char *utf8;
+ int vt, kb = K_XLATE;
+ struct vt_mode mode = { 0 };
+
+ vt = session_open_vt(s);
+ if (vt < 0)
+ return;
+
+ sd_event_source_unref(s->vt_source);
+ s->vt_source = NULL;
+
+ ioctl(vt, KDSETMODE, KD_TEXT);
+
+ if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
+ kb = K_UNICODE;
+ ioctl(vt, KDSKBMODE, kb);
+
+ mode.mode = VT_AUTO;
+ ioctl(vt, VT_SETMODE, &mode);
+
+ close_nointr_nofail(vt);
+ s->vtfd = -1;
+}
+
bool session_is_controller(Session *s, const char *sender) {
assert(s);
@@ -990,6 +1092,9 @@ static void session_swap_controller(Session *s, char *name) {
* dbus signals. */
while ((sd = hashmap_first(s->devices)))
session_device_free(sd);
+
+ if (!name)
+ session_restore_vt(s);
}
s->controller = name;
@@ -1020,6 +1125,16 @@ int session_set_controller(Session *s, const char *sender, bool force) {
session_swap_controller(s, t);
+ /* When setting a session controller, we forcibly mute the VT and set
+ * it into graphics-mode. Applications can override that by changing
+ * VT state after calling TakeControl(). However, this serves as a good
+ * default and well-behaving controllers can now ignore VTs entirely.
+ * Note that we reset the VT on ReleaseControl() and if the controller
+ * exits.
+ * If logind crashes/restarts, we restore the controller during restart
+ * or reset the VT in case it crashed/exited, too. */
+ session_mute_vt(s);
+
return 0;
}
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index f7a9dbc..aab39b7 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -89,8 +89,10 @@ struct Session {
char *scope;
char *scope_job;
- int vtnr;
Seat *seat;
+ int vtnr;
+ int vtfd;
+ sd_event_source *vt_source;
pid_t leader;
uint32_t audit_id;
@@ -162,6 +164,9 @@ SessionClass session_class_from_string(const char *s) _pure_;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
+void session_mute_vt(Session *s);
+void session_restore_vt(Session *s);
+
bool session_is_controller(Session *s, const char *sender);
int session_set_controller(Session *s, const char *sender, bool force);
void session_drop_controller(Session *s);
commit 6d33772f9ae6769c557e2267d16b7d31f67db914
Author: David Herrmann <dh.herrmann at gmail.com>
Date: Thu Nov 28 14:58:57 2013 +0100
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.
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index d343373..0e1c40b 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -239,6 +239,9 @@ int session_save(Session *s) {
(unsigned long long) s->timestamp.realtime,
(unsigned long long) s->timestamp.monotonic);
+ if (s->controller)
+ fprintf(f, "CONTROLLER=%s\n", s->controller);
+
fflush(f);
if (ferror(f) || rename(temp_path, s->state_file) < 0) {
@@ -263,7 +266,8 @@ int session_load(Session *s) {
*class = NULL,
*uid = NULL,
*realtime = NULL,
- *monotonic = NULL;
+ *monotonic = NULL,
+ *controller = NULL;
int k, r;
@@ -287,6 +291,7 @@ int session_load(Session *s) {
"UID", &uid,
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
+ "CONTROLLER", &controller,
NULL);
if (r < 0) {
@@ -387,6 +392,11 @@ int session_load(Session *s) {
s->timestamp.monotonic = l;
}
+ if (controller) {
+ if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
+ session_set_controller(s, controller, false);
+ }
+
return r;
}
@@ -967,6 +977,25 @@ bool session_is_controller(Session *s, const char *sender) {
return streq_ptr(s->controller, sender);
}
+static void session_swap_controller(Session *s, char *name) {
+ SessionDevice *sd;
+
+ if (s->controller) {
+ manager_drop_busname(s->manager, s->controller);
+ free(s->controller);
+ s->controller = NULL;
+
+ /* Drop all devices as they're now unused. Do that after the
+ * controller is released to avoid sending out useles
+ * dbus signals. */
+ while ((sd = hashmap_first(s->devices)))
+ session_device_free(sd);
+ }
+
+ s->controller = name;
+ session_save(s);
+}
+
int session_set_controller(Session *s, const char *sender, bool force) {
char *t;
int r;
@@ -989,28 +1018,18 @@ int session_set_controller(Session *s, const char *sender, bool force) {
return r;
}
- session_drop_controller(s);
+ session_swap_controller(s, t);
- s->controller = t;
return 0;
}
void session_drop_controller(Session *s) {
- SessionDevice *sd;
-
assert(s);
if (!s->controller)
return;
- manager_drop_busname(s->manager, s->controller);
- free(s->controller);
- s->controller = NULL;
-
- /* Drop all devices as they're now unused. Do that after the controller
- * is released to avoid sending out useles dbus signals. */
- while ((sd = hashmap_first(s->devices)))
- session_device_free(sd);
+ session_swap_controller(s, NULL);
}
static const char* const session_state_table[_SESSION_STATE_MAX] = {
commit d1107170f9e0fa2cb6e8d18586a003f0d96abfc3
Author: David Herrmann <dh.herrmann at gmail.com>
Date: Thu Nov 28 14:51:40 2013 +0100
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.
diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c
index b2ef8cc..592bcf2 100644
--- a/src/login/logind-session-device.c
+++ b/src/login/logind-session-device.c
@@ -162,7 +162,7 @@ static int session_device_open(SessionDevice *sd, bool active) {
* state. */
r = sd_drmsetmaster(fd);
if (r < 0) {
- close(fd);
+ close_nointr(fd);
return r;
}
} else {
@@ -209,7 +209,7 @@ static int session_device_start(SessionDevice *sd) {
r = session_device_open(sd, true);
if (r < 0)
return r;
- close_nointr_nofail(sd->fd);
+ close_nointr(sd->fd);
sd->fd = r;
break;
case DEVICE_TYPE_UNKNOWN:
@@ -407,7 +407,7 @@ void session_device_free(SessionDevice *sd) {
session_device_stop(sd);
session_device_notify(sd, SESSION_DEVICE_RELEASE);
- close_nointr_nofail(sd->fd);
+ close_nointr(sd->fd);
LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
commit 5fd38859b30b95008e483109578c7fef2b5072f3
Author: David Herrmann <dh.herrmann at gmail.com>
Date: Thu Nov 28 14:50:19 2013 +0100
bus: add bus_name_has_owner() helper
Small helper to run a synchronous "NameHasOwner" request on the
dbus-daemon.
diff --git a/src/libsystemd-bus/bus-util.c b/src/libsystemd-bus/bus-util.c
index 2daf8c1..7a21975 100644
--- a/src/libsystemd-bus/bus-util.c
+++ b/src/libsystemd-bus/bus-util.c
@@ -103,6 +103,32 @@ int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t
return 0;
}
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
+ int r, has_owner = 0;
+
+ assert(c);
+ assert(name);
+
+ r = sd_bus_call_method(c,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/dbus",
+ "org.freedesktop.DBus",
+ "NameHasOwner",
+ error,
+ &rep,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(rep, 'b', &has_owner);
+ if (r < 0)
+ return sd_bus_error_set_errno(error, r);
+
+ return has_owner;
+}
+
int bus_verify_polkit(
sd_bus *bus,
sd_bus_message *m,
diff --git a/src/libsystemd-bus/bus-util.h b/src/libsystemd-bus/bus-util.h
index 20739a9..38d468e 100644
--- a/src/libsystemd-bus/bus-util.h
+++ b/src/libsystemd-bus/bus-util.h
@@ -56,6 +56,8 @@ int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name);
int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout);
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
+
int bus_check_peercred(sd_bus *c);
int bus_verify_polkit(sd_bus *bus, sd_bus_message *m, const char *action, bool interactive, bool *_challenge, sd_bus_error *e);
More information about the systemd-commits
mailing list