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