logind: make sure there's always a getty available on TTY6

Previously, if X allocated all 6 TTYs (for multi-session for example) no
getty would be available anymore to guarantee console-based logins.

With the new ReserveVT= switch in logind.conf we can now choose one VT
(6 by default) that will always be subject to autovt-style activation,
i.e. we'll always have a getty on TTY6, and X will never take possession
of it.
This commit is contained in:
Lennart Poettering 2012-09-17 12:39:16 +02:00
parent 4db17f291c
commit 98a77df5fe
6 changed files with 92 additions and 16 deletions

4
TODO
View File

@ -55,10 +55,10 @@ F18:
* selinux: merge systemd selinux access controls (dwalsh)
* make logind reserve tty6 or so for text logins, so that gdm never picks it up
Features:
* move cryptsetup key caching into kernel keyctl?
* make nspawn work without terminal
* hw watchdog: optionally try to use the preset watchdog timeout instead of always overriding it

View File

@ -71,14 +71,15 @@
<listitem><para>Takes a positive
integer. Configures how many virtual
terminals to allocate by default that
-- when switched to --
terminals (VTs) to allocate by default
that -- when switched to and
previously unused --
<literal>autovt</literal> services are
automatically spawned on. These
services are instantiated from the
template
template unit
<filename>autovt@.service</filename>
with the virtual terminal TTY name,
for the respective VT TTY name,
e.g. <filename>autovt@tty4.service</filename>. By
default
<filename>autovt@.service</filename>
@ -86,12 +87,46 @@
<filename>getty@.service</filename>,
i.e. login prompts are started
dynamically as the user switches to
unused virtual terminals. This
parameter hence controls how many
gettys are available on the virtual
terminals. Defaults to 6. When set to
unused virtual terminals. Hence, this
parameter controls how many login
<literal>gettys</literal> are
available on the VTs. If a VT is
already used by some other subsystem
(for example a graphical login) this
kind of activation will not be
attempted. Note that the VT configured
in <varname>ReserveVT=</varname> is
always subject to this kind of
activation, even if it is not one of
VTs configured with the
<varname>NAutoVTs=</varname>
directive. Defaults to 6. When set to
0, automatic spawning of
<literal>autovt</literal> services is
disabled. </para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ReserveVT=</varname></term>
<listitem><para>Takes a positive
integer. Configures the number of one
virtual terminal that shall
unconditionally be reserved for
<filename>autovt@.service</filename>
activation (see above). The VT
selected with this option will be
marked busy unconditionally so that no
other subsystem will allocate it. This
functionality is useful to ensure that
regardless how many VTs are allocated
by other subsystems one login
<literal>getty</literal> is always
available. Defaults to 6 (with other
words: there'll always be a
<literal>getty</literal> available on
Alt-F6.). When set to 0, VT
reservation is
disabled.</para></listitem>
</varlistentry>

View File

@ -15,6 +15,7 @@ struct ConfigPerfItem;
%includes
%%
Login.NAutoVTs, config_parse_unsigned, 0, offsetof(Manager, n_autovts)
Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt)
Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes)
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)

View File

@ -50,8 +50,10 @@ Manager *manager_new(void) {
m->udev_vcsa_fd = -1;
m->udev_button_fd = -1;
m->epoll_fd = -1;
m->reserve_vt_fd = -1;
m->n_autovts = 6;
m->reserve_vt = 6;
m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->handle_power_key = HANDLE_NO_SESSION;
m->handle_sleep_key = HANDLE_TTY_SESSION;
@ -166,6 +168,9 @@ void manager_free(Manager *m) {
if (m->epoll_fd >= 0)
close_nointr_nofail(m->epoll_fd);
if (m->reserve_vt_fd >= 0)
close_nointr_nofail(m->reserve_vt_fd);
strv_free(m->controllers);
strv_free(m->reset_controllers);
strv_free(m->kill_only_users);
@ -948,20 +953,28 @@ int manager_spawn_autovt(Manager *m, int vtnr) {
assert(m);
assert(vtnr >= 1);
if ((unsigned) vtnr > m->n_autovts)
if ((unsigned) vtnr > m->n_autovts &&
(unsigned) vtnr != m->reserve_vt)
return 0;
r = vt_is_busy(vtnr);
if (r < 0)
return r;
else if (r > 0)
return -EBUSY;
if ((unsigned) vtnr != m->reserve_vt) {
/* If this is the reserved TTY, we'll start the getty
* on it in any case, but otherwise only if it is not
* busy. */
r = vt_is_busy(vtnr);
if (r < 0)
return r;
else if (r > 0)
return -EBUSY;
}
if (asprintf(&name, "autovt@tty%i.service", vtnr) < 0) {
log_error("Could not allocate service name.");
r = -ENOMEM;
goto finish;
}
r = bus_method_call_with_reply (
m->bus,
"org.freedesktop.systemd1",
@ -980,6 +993,26 @@ finish:
return r;
}
static int manager_reserve_vt(Manager *m) {
_cleanup_free_ char *p = NULL;
assert(m);
if (m->reserve_vt <= 0)
return 0;
if (asprintf(&p, "/dev/tty%u", m->reserve_vt) < 0)
return log_oom();
m->reserve_vt_fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (m->reserve_vt_fd < 0) {
log_warning("Failed to pin reserved VT: %m");
return -errno;
}
return 0;
}
int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session) {
Session *s;
char *p;
@ -1450,6 +1483,9 @@ int manager_startup(Manager *m) {
/* Remove stale objects before we start them */
manager_gc(m, false);
/* Reserve the special reserved VT */
manager_reserve_vt(m);
/* And start everything */
HASHMAP_FOREACH(seat, m->seats, i)
seat_start(seat);

View File

@ -9,6 +9,7 @@
[Login]
#NAutoVTs=6
#ReserveVT=6
#KillUserProcesses=no
#KillOnlyUsers=
#KillExcludeUsers=root

View File

@ -68,6 +68,9 @@ struct Manager {
unsigned n_autovts;
unsigned reserve_vt;
int reserve_vt_fd;
Seat *vtconsole;
char *cgroup_path;