diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/activation-scripts/05-serial_console.py | 52 | ||||
| -rwxr-xr-x | src/conf_mode/system_console.py | 33 | ||||
| -rw-r--r-- | src/migration-scripts/system/31-to-32 | 38 | ||||
| -rwxr-xr-x | src/op_mode/image_installer.py | 58 |
4 files changed, 126 insertions, 55 deletions
diff --git a/src/activation-scripts/05-serial_console.py b/src/activation-scripts/05-serial_console.py index 93e381d17..ccda2ac4e 100755 --- a/src/activation-scripts/05-serial_console.py +++ b/src/activation-scripts/05-serial_console.py @@ -13,45 +13,31 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see <http://www.gnu.org/licenses/>. -import re - -from typing import Optional -from typing import Tuple - from vyos.configtree import ConfigTree -from vyos.utils.file import read_file from vyos.system.image import is_live_boot +from vyos.utils.kernel import get_kernel_serial_console +from vyos.utils.serial import is_tty base = ['system', 'console', 'device'] -def get_kernel_serial_console() -> Tuple[Optional[str], Optional[str]]: - """ - Extract the serial console device and speed setting from the kernel - command line. - """ - device = speed = None - CMDLINE_CONSOLE_RE = re.compile( - r'(?:^|\s)console=(?P<device>tty(?:S|AMA)\d+),(?P<speed>\d+)(?=\s|$)' - ) - kernel_cmdline = read_file('/proc/cmdline') - if m := CMDLINE_CONSOLE_RE.search(kernel_cmdline): - device = m.group("device") # "ttyS0" - speed = m.group("speed") # Baud rate/speed, e.g. "115200" - - return (device, speed) - def activate(config: ConfigTree): - # Configure the kernel serial console only once during live boot. - # During installation, the user can define the console. If this is not - # limited to live boot, the serial interface will always be re-added during - # system boot, even if it was removed from config.boot. + # Configure the kernel serial console only once during live boot. During + # installation, the user can define the console type (VT TTY or serial + # TTY). If this is not limited to live boot, the serial interface will + # always be re-added during system boot, even if it was removed from + # config.boot. if not is_live_boot(): return - (device, speed) = get_kernel_serial_console() - if device and speed: - # The kernel was booted with a configured serial console, but no - # console is configured in the CLI; align/fix the CLI configuration. - if not config.exists(base + [device]): - config.set(base + [device, 'speed'], value=speed) - config.set_tag(base) + # Parse current kernel cmdline and continue only for valid serial console + # data. Prevent writing incomplete/invalid console settings to config.boot. + console_type, console_num, console_speed = get_kernel_serial_console() + device = f'{console_type}{console_num}' + if not is_tty(device) or not console_speed: + return + + # The kernel was booted with a configured serial console, but no console + # is configured via CLI; align/fix the CLI configuration. + if not config.exists(base + [device]): + config.set(base + [device, 'speed'], value=console_speed) + config.set_tag(base) diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 5d89eeba7..50e5e607b 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -30,6 +30,8 @@ airbag.enable() by_bus_dir = '/dev/serial/by-bus' +default_tty_console = ('tty', '0', '') + def get_config(config=None): if config: conf = config @@ -57,7 +59,8 @@ def verify(console): if not console or 'device' not in console: return None - for device in console['device']: + kernel_consoles: list = [] + for device, device_config in console['device'].items(): if device.startswith('usb'): # It is much easiert to work with the native ttyUSBn name when using # getty, but that name may change across reboots - depending on the @@ -70,6 +73,11 @@ def verify(console): raise ConfigError(f'Device {device} does not support being used as tty') if not is_tty(device, warning=True): Warning(f'Device "{device}" used for console is not a TTY!') + if 'kernel' in device_config: + kernel_consoles.append(device) + + if len(kernel_consoles) > 1: + raise ConfigError('Only one device can be used as Kernel output console!') return None @@ -82,7 +90,7 @@ def generate(console): os.unlink(os.path.join(root, basename)) if not console or 'device' not in console: - grub_util.update_serial_console('', '', '') + grub_util.update_serial_console(*default_tty_console) return None # replace keys in the config for ttyUSB items to use them in `apply()` later @@ -102,6 +110,7 @@ def generate(console): else: raise ConfigError(f'Device {device} does not support being used as tty') + use_serial_console = False for device, device_config in console['device'].items(): # Do not render getty configuration if specified device is not a TTY. if not is_tty(device): @@ -113,17 +122,15 @@ def generate(console): render(config_file, 'getty/serial-getty.service.j2', device_config) os.symlink(config_file, getty_wants_symlink) - # GRUB - use first defined serial console to populate Kernel console= parameter - device = None - if 'device' in console and len(console['device']) > 0: - # We use the first device for the Kernel console - console_device = next(iter(console['device'])) - console_speed = console['device'][console_device]['speed'] - - # get console type ("ttyS" or "ttyAMA") from console_device (e.g. "ttyS0") - console_type = console_device.translate(str.maketrans('', '', '0123456789')) - console_num = ''.join(ch for ch in console_device if ch.isdigit()) - grub_util.update_serial_console(console_type, console_num, console_speed) + if 'kernel' in device_config: + # get console type ("ttyS" or "ttyAMA") from device (e.g. "ttyS0") + console_type = device.rstrip('0123456789') + console_num = device[len(console_type):] + grub_util.update_serial_console(console_type, console_num, device_config['speed']) + use_serial_console = True + + if not use_serial_console: + grub_util.update_serial_console(*default_tty_console) return None diff --git a/src/migration-scripts/system/31-to-32 b/src/migration-scripts/system/31-to-32 new file mode 100644 index 000000000..1688b44ed --- /dev/null +++ b/src/migration-scripts/system/31-to-32 @@ -0,0 +1,38 @@ +# Copyright (C) VyOS Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Serial + +from vyos.configtree import ConfigTree +from vyos.system import disk +from vyos.system.grub import CFG_VYOS_VARS +from vyos.system.grub import vars_read + +serial_console = 'ttyS0' +base = ['system', 'console'] + +def migrate(config: ConfigTree) -> None: + if not base: + return + + root_dir = disk.find_persistence() + vars_file: str = f'{root_dir}/{CFG_VYOS_VARS}' + vars_current: dict[str, str] = vars_read(vars_file) + # Check if VyOS installation uses serial boot console + if vars_current['console_type'] == 'ttyS': + # In the past we only supported ttyS0 as boot console, that's why we + # can hardcode it ... + if config.exists(base + ['device', serial_console]): + config.set(base + ['device', serial_console, 'kernel']) diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index fb3cdda9e..27d8214c3 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -17,7 +17,8 @@ # You should have received a copy of the GNU General Public License along with # VyOS. If not, see <https://www.gnu.org/licenses/>. -from argparse import ArgumentParser, Namespace +from argparse import ArgumentParser +from argparse import Namespace from pathlib import Path from shutil import copy from shutil import chown @@ -616,6 +617,49 @@ def configure_authentication(config_file: str, password: str) -> None: with open(config_file, 'w') as f: f.write(config.to_string()) +def configure_serial_console(config_file: str, console_type: str) -> None: + """Apply serial console settings to config.boot from kernel cmdline. + + This overlaps with 05-serial_console.py activation logic, but that script + only runs during live boot. During installation, the user may pick a + different source config, so serial console settings must be written to + the final target config explicitly. + + Behavior: + - Reads the kernel serial console device/speed from the current boot cmdline. + - If the detected device is a valid tty, writes: + system console device <TTY> speed <rate> + - If "console_type == 'S'", also writes: + system console device <TTY> kernel + + Args: + config_file (str): path of target config file + console_type (str): 'K' (KVM/tty) or 'S' (serial) + """ + from vyos.utils.serial import is_tty + from vyos.utils.kernel import get_kernel_serial_console + + # Parse current kernel cmdline and continue only for valid serial console + # data. Prevent writing incomplete/invalid console settings to config.boot. + k_console_type, k_console_num, k_console_speed = get_kernel_serial_console() + device = f'{k_console_type}{k_console_num}' + if not is_tty(device) or not k_console_speed: + return + + base = ['system', 'console', 'device'] + config_string = read_file(config_file) + config = ConfigTree(config_string) + config.set(base + [device, 'speed'], value=k_console_speed) + config.set_tag(base) + + # Only mark this device as kernel boot console when console_type 'S' for + # serial was defined by user. + if console_type == 'S': + config.set(base + [device, 'kernel']) + + with open(config_file, 'w') as f: + f.write(config.to_string()) + def validate_signature(file_path: str, sign_type: str) -> None: """Validate a file by signature and delete a signature file @@ -769,10 +813,8 @@ def console_hint() -> str: path = '/dev/tty' name = Path(path).name - if name in ['ttyS0']: + if name.startswith(('ttyS', 'ttyAMA')): return 'S' - elif name in ['ttyAMA0']: - return 'A' else: return 'K' @@ -924,11 +966,7 @@ def install_image() -> None: print(MSG_WARN_PASSWORD_CONFIRM) # ask for default console - console_dict: dict[str, str] = {'K': 'tty'} - if flavor_sercon_type: - tmp: str = flavor_sercon_type[-1] # get "S" from "ttyS" and "A" from "ttyAMA" - console_dict.update({tmp: flavor_sercon_type}) - + console_dict: dict[str, str] = {'K': 'tty', 'S': flavor_sercon_type} console_type: str = ask_input(MSG_INPUT_CONSOLE_TYPE, default=console_hint(), valid_responses=console_dict.keys()) @@ -977,6 +1015,8 @@ def install_image() -> None: copy(default_config, f'{target_config_dir}/config.boot') configure_authentication(f'{target_config_dir}/config.boot', user_password) + configure_serial_console(f'{target_config_dir}/config.boot', + console_type) Path(f'{target_config_dir}/.vyatta_config').touch() # create a persistence.conf |
