getty-generator: add support for fine-grained control of execution modes

This makes the systemd.getty_auto= kernel command line option and the
$SYSTEMD_GETTY_AUTO environment variable takes the list of classes of
services: credential, container, console, builtin.

This also add getty.auto credential that can take the same value as the
kernel command line option.

Closes #37928.

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
This commit is contained in:
Allison Karlitskaya
2025-06-24 10:15:14 +02:00
committed by Yu Watanabe
parent 4e346e10d2
commit 3a883e89bc
3 changed files with 204 additions and 72 deletions

View File

@@ -64,11 +64,35 @@
<varlistentry>
<term><varname>systemd.getty_auto=</varname></term>
<listitem><para>this options take an optional boolean argument, and default to yes.
The generator is enabled by default, and a false value may be used to disable it.
</para>
<listitem>
<para>This kernel command line option may be used to control the execution mode of the generator.
Takes an optional boolean argument. Since v258, this also takes comma-separated list of special
values: <literal>credential</literal>, <literal>container</literal>, <literal>console</literal>,
and <literal>builtin</literal>.</para>
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
<para>When <literal>credential</literal> is specified, the two credentials
<varname>getty.ttys.serial</varname> and <varname>getty.ttys.container</varname> will be parsed.
See System Credentials section below for more details.</para>
<para>When <literal>container</literal> is specified, <filename>console-getty.service</filename>
and <filename>container-getty@.service</filename> will be enabled when the system is running in a
container. This option will be ignored when the system is not in a container.</para>
<para>When <literal>console</literal> is specified, <filename>serial-getty@.service</filename> for
active kernel consoles will be enabled. This option will be ignored when the system is running in a
container.</para>
<para>When <literal>builtins</literal> is specified, <filename>serial-getty@.service</filename> for
available virtualizer consoles will be enabled. This option will be ignored when the system is
running in a container.</para>
<para>When yes, the above four options will be enabled. When no, all options are disabled and no
service will be enabled. When the kernel command line option is specified without an argument,
defaults to yes. The generator is enabled by default, and a false value may be used to disable it.
</para>
<xi:include href="version-info.xml" xpointer="v250"/>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -80,11 +104,12 @@
<varlistentry>
<term><varname>$SYSTEMD_GETTY_AUTO</varname></term>
<listitem><para>This variable takes an optional boolean argument, and default to yes.
The generator is enabled by default, and a false value may be used to disable it.
</para>
<listitem>
<para>This environment variable may be used to control the execution mode of the generator.
Takes the same value as <varname>systemd.getty_auto=</varname> kernel command line option.</para>
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
<xi:include href="version-info.xml" xpointer="v250"/>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -93,6 +118,17 @@
<title>System Credentials</title>
<variablelist class='system-credentials'>
<varlistentry>
<term><varname>getty.auto</varname></term>
<listitem>
<para>The system credential may be used to control the execution mode of the generator.
Takes the same value as <varname>systemd.getty_auto=</varname> kernel command line option.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>getty.ttys.serial</varname></term>
<term><varname>getty.ttys.container</varname></term>
@@ -101,7 +137,7 @@
TTYs. The two credentials should contain a newline-separated list of TTY names to spawn instances of
<filename>serial-getty@.service</filename> (in case of <varname>getty.ttys.serial</varname>) and
<filename>container-getty@.service</filename> (in case of <varname>getty.ttys.container</varname>)
on.</para>
on. Any lines starting with a <literal>#</literal> will be ignored.</para>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>

View File

@@ -287,6 +287,15 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>getty.auto</varname></term>
<listitem><para>Used for controlling the execution mode of <filename>systemd-getty-generator</filename>. See
<citerefentry><refentrytitle>systemd-getty-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>getty.ttys.serial</varname></term>
<term><varname>getty.ttys.container</varname></term>

View File

@@ -21,8 +21,17 @@
#include "unit-name.h"
#include "virt.h"
typedef enum {
GETTY_SOURCE_NONE = 0,
GETTY_SOURCE_CREDENTIAL = 1 << 0,
GETTY_SOURCE_CONTAINER = 1 << 1,
GETTY_SOURCE_CONSOLE = 1 << 2,
GETTY_SOURCE_BUILTIN = 1 << 3,
GETTY_SOURCE_ALL = GETTY_SOURCE_CREDENTIAL | GETTY_SOURCE_CONTAINER | GETTY_SOURCE_CONSOLE | GETTY_SOURCE_BUILTIN,
} GettySourceFlag;
static const char *arg_dest = NULL;
static bool arg_enabled = true;
static GettySourceFlag arg_getty_sources = GETTY_SOURCE_ALL;
static int add_getty_impl(const char *tty, const char *path, const char *type, const char *unit_path) {
int r;
@@ -166,24 +175,101 @@ static int add_credential_gettys(void) {
return 0;
}
static int parse_getty_sources(const char *s, GettySourceFlag *ret) {
int r;
assert(ret);
if (isempty(s)) {
*ret = GETTY_SOURCE_ALL;
return 0;
}
r = parse_boolean(s);
if (r >= 0) {
*ret = r ? GETTY_SOURCE_ALL : GETTY_SOURCE_NONE;
return 0;
}
static struct {
GettySourceFlag flag;
const char *str;
} table[] = {
{ GETTY_SOURCE_CREDENTIAL, "credential", },
{ GETTY_SOURCE_CONTAINER, "container", },
{ GETTY_SOURCE_CONSOLE, "console", },
{ GETTY_SOURCE_BUILTIN, "builtin", },
};
GettySourceFlag flags = 0;
for (const char *p = s;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, ",", /* flags = */ 0);
if (r < 0)
return r;
if (r == 0)
break;
bool found = false;
FOREACH_ELEMENT(i, table)
if (streq(word, i->str)) {
flags |= i->flag;
found = true;
break;
}
if (!found)
return -EINVAL;
}
*ret = flags;
return 0;
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
assert(key);
if (proc_cmdline_key_streq(key, "systemd.getty_auto")) {
r = value ? parse_boolean(value) : 1;
r = parse_getty_sources(value, &arg_getty_sources);
if (r < 0)
log_warning_errno(r, "Failed to parse getty_auto switch \"%s\", ignoring: %m", value);
else
arg_enabled = r;
log_warning_errno(r, "Failed to parse systemd.getty_auto= kernel command line option, ignoring: %s", value);
}
return 0;
}
static void parse_env(void) {
_cleanup_free_ char *value = NULL;
int r;
r = getenv_for_pid(1, "SYSTEMD_GETTY_AUTO", &value);
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO environment variable, ignoring: %m");
else if (r > 0) {
r = parse_getty_sources(value, &arg_getty_sources);
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO environment variable, ignoring: %s", value);
}
}
static void parse_credentials(void) {
_cleanup_free_ char *value = NULL;
int r;
r = read_credential_with_decryption("getty.auto", (void**) &value, /* ret_size = */ NULL);
if (r < 0)
log_debug_errno(r, "Failed to read credential 'getty.auto', ignoring: %m");
else if (r > 0) {
r = parse_getty_sources(value, &arg_getty_sources);
if (r < 0)
log_warning_errno(r, "Invalid 'getty.auto' credential, ignoring: %s", value);
}
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
_cleanup_free_ char *getty_auto = NULL;
int r;
assert_se(arg_dest = dest);
@@ -197,72 +283,73 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
r = getenv_for_pid(1, "SYSTEMD_GETTY_AUTO", &getty_auto);
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO environment variable, ignoring: %m");
else if (r > 0) {
r = parse_boolean(getty_auto);
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO value \"%s\", ignoring: %m", getty_auto);
else
arg_enabled = r;
}
parse_env();
parse_credentials();
if (!arg_enabled) {
if (arg_getty_sources == GETTY_SOURCE_NONE) {
log_debug("Disabled, exiting.");
return 0;
}
r = add_credential_gettys();
if (r < 0)
return r;
if (detect_container() > 0)
/* Add console shell and look at $container_ttys, but don't do add any
* further magic if we are in a container. */
return run_container();
/* Automatically add in a serial getty on all active kernel consoles */
_cleanup_strv_free_ char **consoles = NULL;
r = get_kernel_consoles(&consoles);
if (r < 0)
log_warning_errno(r, "Failed to get active kernel consoles, ignoring: %m");
else if (r > 0)
STRV_FOREACH(i, consoles) {
/* We assume that gettys on virtual terminals are started via manual configuration
* and do this magic only for non-VC terminals. */
if (tty_is_vc(*i))
continue;
if (verify_tty(*i) < 0)
continue;
r = add_serial_getty(*i);
if (r < 0)
return r;
}
/* Automatically add a serial getty to each available virtualizer console. */
FOREACH_STRING(j,
"hvc0",
"xvc0",
"hvsi0",
"sclp_line0",
"ttysclp0",
"3270/tty1") {
_cleanup_free_ char *p = NULL;
p = path_join("/dev", j);
if (!p)
return log_oom();
if (access(p, F_OK) < 0)
continue;
r = add_serial_getty(j);
if (FLAGS_SET(arg_getty_sources, GETTY_SOURCE_CREDENTIAL)) {
r = add_credential_gettys();
if (r < 0)
return r;
}
if (detect_container() > 0) {
/* Add console shell and look at $container_ttys, but don't do add any
* further magic if we are in a container. */
if (FLAGS_SET(arg_getty_sources, GETTY_SOURCE_CONTAINER))
return run_container();
return 0;
}
/* Automatically add in a serial getty on all active kernel consoles */
if (FLAGS_SET(arg_getty_sources, GETTY_SOURCE_CONSOLE)) {
_cleanup_strv_free_ char **consoles = NULL;
r = get_kernel_consoles(&consoles);
if (r < 0)
log_warning_errno(r, "Failed to get active kernel consoles, ignoring: %m");
else if (r > 0)
STRV_FOREACH(i, consoles) {
/* We assume that gettys on virtual terminals are started via manual configuration
* and do this magic only for non-VC terminals. */
if (tty_is_vc(*i))
continue;
if (verify_tty(*i) < 0)
continue;
r = add_serial_getty(*i);
if (r < 0)
return r;
}
}
/* Automatically add a serial getty to each available virtualizer console. */
if (FLAGS_SET(arg_getty_sources, GETTY_SOURCE_BUILTIN))
FOREACH_STRING(j,
"hvc0",
"xvc0",
"hvsi0",
"sclp_line0",
"ttysclp0",
"3270/tty1") {
_cleanup_free_ char *p = NULL;
p = path_join("/dev", j);
if (!p)
return log_oom();
if (access(p, F_OK) < 0)
continue;
r = add_serial_getty(j);
if (r < 0)
return r;
}
return 0;
}