forked from pool/systemd
247 lines
8.1 KiB
Diff
247 lines
8.1 KiB
Diff
Based on 92683ad2e28c79891e4123d9a421b018dc58870c Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 11 Aug 2014 18:17:54 +0200
|
|
Subject: [PATCH] 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.
|
|
---
|
|
src/libsystemd/sd-event/sd-event.c | 5 +-
|
|
src/login/logind-session.c | 26 +------------
|
|
src/login/logind-session.h | 1
|
|
src/login/logind.c | 70 +++++++++++++++++++++++++++++++++++++
|
|
src/shared/util.c | 18 +++++++++
|
|
src/shared/util.h | 1
|
|
6 files changed, 94 insertions(+), 27 deletions(-)
|
|
|
|
--- src/libsystemd/sd-event/sd-event.c
|
|
+++ src/libsystemd/sd-event/sd-event.c 2014-08-26 11:02:54.500683967 +0000
|
|
@@ -839,7 +839,6 @@ _public_ int sd_event_add_signal(
|
|
assert_return(sig > 0, -EINVAL);
|
|
assert_return(sig < _NSIG, -EINVAL);
|
|
assert_return(callback, -EINVAL);
|
|
- assert_return(ret, -EINVAL);
|
|
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
|
assert_return(!event_pid_changed(e), -ECHILD);
|
|
|
|
@@ -877,7 +876,9 @@ _public_ int sd_event_add_signal(
|
|
}
|
|
}
|
|
|
|
- *ret = s;
|
|
+ if (ret)
|
|
+ *ret = s;
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
--- src/login/logind-session.c
|
|
+++ src/login/logind-session.c 2014-08-25 17:22:46.000000000 +0000
|
|
@@ -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);
|
|
}
|
|
@@ -966,19 +964,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_mute_vt(Session *s) {
|
|
int vt, r;
|
|
struct vt_mode mode = { 0 };
|
|
- sigset_t mask;
|
|
|
|
if (s->vtnr < 1)
|
|
return 0;
|
|
@@ -1008,20 +996,12 @@ int session_mute_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;
|
|
@@ -1045,8 +1025,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')
|
|
--- src/login/logind-session.h
|
|
+++ src/login/logind-session.h 2014-08-25 00:00:00.000000000 +0000
|
|
@@ -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;
|
|
--- src/login/logind.c
|
|
+++ src/login/logind.c 2014-08-26 11:18:41.422235366 +0000
|
|
@@ -25,6 +25,7 @@
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <linux/vt.h>
|
|
+#include <sys/ioctl.h>
|
|
#include <sys/timerfd.h>
|
|
|
|
#include "sd-daemon.h"
|
|
@@ -714,6 +715,47 @@ static int manager_connect_bus(Manager *
|
|
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;
|
|
|
|
@@ -744,6 +786,34 @@ static int manager_connect_console(Manag
|
|
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;
|
|
}
|
|
|
|
--- src/shared/util.c
|
|
+++ src/shared/util.c 2014-08-26 10:20:14.000000000 +0000
|
|
@@ -2434,6 +2434,24 @@ void sigset_add_many(sigset_t *ss, ...)
|
|
va_end(ap);
|
|
}
|
|
|
|
+int sigprocmask_many(int how, ...) {
|
|
+ va_list ap;
|
|
+ sigset_t ss;
|
|
+ int sig;
|
|
+
|
|
+ assert_se(sigemptyset(&ss) == 0);
|
|
+
|
|
+ va_start(ap, how);
|
|
+ while ((sig = va_arg(ap, int)) > 0)
|
|
+ assert_se(sigaddset(&ss, sig) == 0);
|
|
+ va_end(ap);
|
|
+
|
|
+ if (sigprocmask(how, &ss, NULL) < 0)
|
|
+ return -errno;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
char* gethostname_malloc(void) {
|
|
struct utsname u;
|
|
|
|
--- src/shared/util.h
|
|
+++ src/shared/util.h 2014-08-26 10:21:08.000000000 +0000
|
|
@@ -390,6 +390,7 @@ char* dirname_malloc(const char *path);
|
|
void rename_process(const char name[8]);
|
|
|
|
void sigset_add_many(sigset_t *ss, ...);
|
|
+int sigprocmask_many(int how, ...);
|
|
|
|
bool hostname_is_set(void);
|
|
|