1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-11 09:18:07 +03:00

login: share VT-signal handler between sessions

sd-event does not allow multiple handlers for a single signal. However,
logind sets up signal handlers for each session with VT_PROCESS set (that
is, it has an active controller). Therefore, registering multiple such
controllers will fail.

Lets make the VT-handler global, as it's mostly trivial, anyway. This way,
the sessions don't have to take care of that and we can simply acknowledge
all VT-switch requests as we always did.
This commit is contained in:
David Herrmann 2014-08-11 18:17:54 +02:00
parent 38de08a7e4
commit 92683ad2e2
Notes: Lennart Poettering 2014-08-19 20:54:36 +02:00
Backport: bugfix
3 changed files with 71 additions and 25 deletions

View File

@ -153,8 +153,6 @@ void session_free(Session *s) {
hashmap_remove(s->manager->sessions, s->id);
s->vt_source = sd_event_source_unref(s->vt_source);
free(s->state_file);
free(s);
}
@ -994,19 +992,9 @@ static int session_open_vt(Session *s) {
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;
}
int session_prepare_vt(Session *s) {
int vt, r;
struct vt_mode mode = { 0 };
sigset_t mask;
if (s->vtnr < 1)
return 0;
@ -1036,20 +1024,12 @@ int session_prepare_vt(Session *s) {
goto error;
}
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, NULL);
r = sd_event_add_signal(s->manager->event, &s->vt_source, SIGUSR1, session_vt_fn, s);
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;
mode.relsig = SIGRTMIN;
mode.acqsig = SIGRTMIN + 1;
r = ioctl(vt, VT_SETMODE, &mode);
if (r < 0) {
r = -errno;
@ -1073,8 +1053,6 @@ void session_restore_vt(Session *s) {
if (vt < 0)
return;
s->vt_source = sd_event_source_unref(s->vt_source);
ioctl(vt, KDSETMODE, KD_TEXT);
if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')

View File

@ -98,7 +98,6 @@ struct Session {
Seat *seat;
unsigned int vtnr;
int vtfd;
sd_event_source *vt_source;
pid_t leader;
uint32_t audit_id;

View File

@ -720,6 +720,47 @@ static int manager_connect_bus(Manager *m) {
return 0;
}
static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo *si, void *data) {
Manager *m = data;
Session *active, *iter;
/*
* We got a VT-switch signal and we have to acknowledge it immediately.
* Preferably, we'd just use m->seat0->active->vtfd, but unfortunately,
* old user-space might run multiple sessions on a single VT, *sigh*.
* Therefore, we have to iterate all sessions and find one with a vtfd
* on the requested VT.
* As only VTs with active controllers have VT_PROCESS set, our current
* notion of the active VT might be wrong (for instance if the switch
* happens while we setup VT_PROCESS). Therefore, read the current VT
* first and then use s->active->vtnr as reference. Note that this is
* not racy, as no further VT-switch can happen as long as we're in
* synchronous VT_PROCESS mode.
*/
assert(m->seat0);
seat_read_active_vt(m->seat0);
active = m->seat0->active;
if (!active || active->vtnr < 1) {
log_warning("Received VT_PROCESS signal without a registered session on that VT.");
return 0;
}
if (active->vtfd >= 0) {
ioctl(active->vtfd, VT_RELDISP, 1);
} else {
LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) {
if (iter->vtnr == active->vtnr && iter->vtfd >= 0) {
ioctl(iter->vtfd, VT_RELDISP, 1);
break;
}
}
}
return 0;
}
static int manager_connect_console(Manager *m) {
int r;
@ -750,6 +791,34 @@ static int manager_connect_console(Manager *m) {
return r;
}
/*
* SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used
* as VT-acquire signal. We ignore any acquire-events (yes, we still
* have to provide a valid signal-number for it!) and acknowledge all
* release events immediately.
*/
if (SIGRTMIN + 1 > SIGRTMAX) {
log_error("Not enough real-time signals available: %u-%u", SIGRTMIN, SIGRTMAX);
return -EINVAL;
}
r = ignore_signals(SIGRTMIN + 1, -1);
if (r < 0) {
log_error("Cannot ignore SIGRTMIN + 1: %s", strerror(-r));
return r;
}
r = sigprocmask_many(SIG_BLOCK, SIGRTMIN, -1);
if (r < 0) {
log_error("Cannot block SIGRTMIN: %s", strerror(-r));
return r;
}
r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
if (r < 0)
return r;
return 0;
}