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-07-23 08:35:51 +09:00
committed by Yu Watanabe
co-authored by Yu Watanabe
parent 4e346e10d2
commit 3a883e89bc
3 changed files with 204 additions and 72 deletions
+150 -63
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;
}