diff --git a/man/systemd-getty-generator.xml b/man/systemd-getty-generator.xml
index 574bf9f86b..d02069e630 100644
--- a/man/systemd-getty-generator.xml
+++ b/man/systemd-getty-generator.xml
@@ -64,11 +64,35 @@
systemd.getty_auto=
- 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.
-
+
+ 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: credential, container, console,
+ and builtin.
-
+ When credential is specified, the two credentials
+ getty.ttys.serial and getty.ttys.container will be parsed.
+ See System Credentials section below for more details.
+
+ When container is specified, console-getty.service
+ and container-getty@.service will be enabled when the system is running in a
+ container. This option will be ignored when the system is not in a container.
+
+ When console is specified, serial-getty@.service for
+ active kernel consoles will be enabled. This option will be ignored when the system is running in a
+ container.
+
+ When builtins is specified, serial-getty@.service for
+ available virtualizer consoles will be enabled. This option will be ignored when the system is
+ running in a container.
+
+ 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.
+
+
+
+
@@ -80,11 +104,12 @@
$SYSTEMD_GETTY_AUTO
- 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.
-
+
+ This environment variable may be used to control the execution mode of the generator.
+ Takes the same value as systemd.getty_auto= kernel command line option.
-
+
+
@@ -93,6 +118,17 @@
System Credentials
+
+ getty.auto
+
+
+ The system credential may be used to control the execution mode of the generator.
+ Takes the same value as systemd.getty_auto= kernel command line option.
+
+
+
+
+
getty.ttys.serial
getty.ttys.container
@@ -101,7 +137,7 @@
TTYs. The two credentials should contain a newline-separated list of TTY names to spawn instances of
serial-getty@.service (in case of getty.ttys.serial) and
container-getty@.service (in case of getty.ttys.container)
- on.
+ on. Any lines starting with a # will be ignored.
diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml
index fd0d12320d..1e26654e8d 100644
--- a/man/systemd.system-credentials.xml
+++ b/man/systemd.system-credentials.xml
@@ -287,6 +287,15 @@
+
+ getty.auto
+
+ Used for controlling the execution mode of systemd-getty-generator. See
+ systemd-getty-generator8 for details.
+
+
+
+
getty.ttys.serial
getty.ttys.container
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 810ee9a2f9..cd9aae51e3 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -21,17 +21,27 @@
#include "unit-name.h"
#include "virt.h"
-static const char *arg_dest = NULL;
-static bool arg_enabled = true;
+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 int add_getty_impl(const char *tty, const char *type, const char *unit_path) {
+static const char *arg_dest = NULL;
+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;
assert(type);
+ assert(path);
assert(unit_path);
if (!filename_is_valid(tty)) {
- log_debug("Invalid %s tty device specified, ignoring: %s", type, tty);
+ log_debug("Invalid %s tty device specified, ignoring: %s", type, path);
return 0;
}
@@ -45,17 +55,23 @@ static int add_getty_impl(const char *tty, const char *type, const char *unit_pa
return generator_add_symlink_full(arg_dest, "getty.target", "wants", unit_path, instance);
}
-static int add_serial_getty(const char *tty) {
- tty = skip_dev_prefix(ASSERT_PTR(tty));
- return add_getty_impl(tty, "serial", SYSTEM_DATA_UNIT_DIR "/serial-getty@.service");
+static int add_serial_getty(const char *path) {
+ const char *tty = skip_dev_prefix(ASSERT_PTR(path));
+ return add_getty_impl(tty, path, "serial", SYSTEM_DATA_UNIT_DIR "/serial-getty@.service");
}
-static int add_container_getty(const char *tty) {
- if (is_path(tty))
- /* Check if it is actually a pty. */
- tty = path_startswith(skip_dev_prefix(tty), "pts/");
+static int add_container_getty(const char *tty_or_path) {
+ const char *tty;
- return add_getty_impl(tty, "container", SYSTEM_DATA_UNIT_DIR "/container-getty@.service");
+ assert(tty_or_path);
+
+ if (is_path(tty_or_path))
+ /* Check if it is actually a pty. */
+ tty = path_startswith(skip_dev_prefix(tty_or_path), "pts/");
+ else
+ tty = tty_or_path;
+
+ return add_getty_impl(tty, tty_or_path, "container", SYSTEM_DATA_UNIT_DIR "/container-getty@.service");
}
static int verify_tty(const char *path) {
@@ -159,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);
@@ -190,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;
}
diff --git a/test/units/TEST-81-GENERATORS.getty-generator.sh b/test/units/TEST-81-GENERATORS.getty-generator.sh
index d1dd22c18e..3899e815de 100755
--- a/test/units/TEST-81-GENERATORS.getty-generator.sh
+++ b/test/units/TEST-81-GENERATORS.getty-generator.sh
@@ -20,20 +20,25 @@ trap at_exit EXIT
test -x "${GENERATOR_BIN:?}"
-if in_container; then
- # Do a limited test in a container, as writing to /dev is usually restrited
- : "getty-generator: \$container_ttys env (container)"
- # In a container we allow only /dev/pts/* ptys
- PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_none() {
+ [[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+}
- # console-getty.service is always pulled in in containers
+check_container() {
link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service"
link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service"
test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
+}
- exit 0
-fi
+check_no_container() {
+ local unit
+
+ for unit in console-getty.service container-getty@0.service container-getty@tty0.service; do
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+ done
+}
DUMMY_ACTIVE_CONSOLES=(
"hvc99"
@@ -52,6 +57,94 @@ DUMMY_CONSOLES=(
"${DUMMY_ACTIVE_CONSOLES[@]}"
"${DUMMY_INACTIVE_CONSOLES[@]}"
)
+
+check_console() {
+ local console unit
+
+ for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
+ done
+ for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+ done
+}
+
+check_no_console() {
+ local console unit
+
+ for console in "${DUMMY_CONSOLES[@]}" /dev/notatty99; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+ done
+}
+
+BUILTINS=(
+ "hvc0"
+ "xvc0"
+ "hvsi0"
+ "sclp_line0"
+ "ttysclp0"
+ "3270/tty1"
+)
+
+check_builtin() {
+ local console unit
+
+ for console in "${BUILTINS[@]}"; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ if [[ -e "/dev/$console" ]]; then
+ link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
+ else
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+ fi
+ done
+}
+
+check_no_builtin() {
+ local console unit
+
+ for console in "${BUILTINS[@]}"; do
+ unit="$(systemd-escape --template serial-getty@.service "$console")"
+ test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+ test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+ done
+}
+
+if in_container; then
+ # Do a limited test in a container, as writing to /dev is usually restrited
+ : "getty-generator: \$container_ttys env (container)"
+ # In a container we allow only /dev/pts/* ptys
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+ check_container
+ check_no_console
+ check_no_builtin
+
+ : "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment (container)"
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0\0SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+ check_none
+
+ : "getty-generator: SYSTEMD_GETTY_AUTO=console in PID1's environment (container)"
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0\0SYSTEMD_GETTY_AUTO=console" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+ check_none
+
+ : "getty-generator: SYSTEMD_GETTY_AUTO=console,builtin in PID1's environment (container)"
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0\0SYSTEMD_GETTY_AUTO=console,builtin" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+ check_none
+
+ : "getty-generator: SYSTEMD_GETTY_AUTO=console,builtin,container in PID1's environment (container)"
+ PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0\0SYSTEMD_GETTY_AUTO=console,builtin,container" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+ check_container
+ check_no_console
+ check_no_builtin
+
+ exit 0
+fi
+
# Create a bunch of dummy consoles
for console in "${DUMMY_CONSOLES[@]}"; do
mknod "/dev/$console" c 4 0
@@ -60,29 +153,55 @@ done
touch /dev/notatty99
# Temporarily replace /sys/class/tty/console/active with our list of dummy
# consoles so getty-generator can process them
-echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles
+echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles
mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active
: "getty-generator: no arguments"
# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out
PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
- unit="$(systemd-escape --template serial-getty@.service "$console")"
- link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
-done
-for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
- unit="$(systemd-escape --template serial-getty@.service "$console")"
- test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
- test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
-done
+check_no_container
+check_console
+check_builtin
: "getty-generator: systemd.getty_auto=0 on kernel cmdline"
SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+check_none
+
+: "getty-generator: systemd.getty_auto=container on kernel cmdline"
+SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=container" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_none
+
+: "getty-generator: systemd.getty_auto=container,console on kernel cmdline"
+SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=container,console" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_no_container
+check_console
+check_no_builtin
+
+: "getty-generator: systemd.getty_auto=container,builtin on kernel cmdline"
+SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=container,builtin" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_no_container
+check_no_console
+check_builtin
: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment"
PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+check_none
+
+: "getty-generator: SYSTEMD_GETTY_AUTO=container in PID1's environment"
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=container" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_none
+
+: "getty-generator: SYSTEMD_GETTY_AUTO=container,console in PID1's environment"
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=container,console" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_no_container
+check_console
+check_no_builtin
+
+: "getty-generator: SYSTEMD_GETTY_AUTO=container,builtin in PID1's environment"
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=container,builtin" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_no_container
+check_no_console
+check_builtin
# Cleanup
umount /sys/class/tty/console/active --lazy