From a75bc85df3da707d75531b001150cc8501573184 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 8 Jun 2020 21:42:09 +0200 Subject: console: T2569: initial implementation with XML and Python Migrate the serial console subsystem to XML and Python. --- interface-definitions/system-console.xml.in | 86 +++++++++++++++++++++ src/conf_mode/system_console.py | 111 ++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 interface-definitions/system-console.xml.in create mode 100755 src/conf_mode/system_console.py diff --git a/interface-definitions/system-console.xml.in b/interface-definitions/system-console.xml.in new file mode 100644 index 000000000..ccaaa51b2 --- /dev/null +++ b/interface-definitions/system-console.xml.in @@ -0,0 +1,86 @@ + + + + + + + Serial console configuration + 100 + + + + + Serial console device name + + ttySN + Serial device name + + + ttyUSBX + USB Serial device name + + + hvc0 + Xen console + + + ^(ttyS|ttyUSB|hvc)[0-9]+$ + + + + + + Console baud rate + + 1200 2400 4800 9600 19200 38400 57600 115200 + + + 1200 + 1200 bps + + + 2400 + 2400 bps + + + 4800 + 4800 bps + + + 9600 + 9600 bps + + + 19200 + 19200 bps + + + 38400 + 38400 bps + + + 57600 + 57600 bps + + + 115200 + 115200 bps + + + (1200|2400|4800|9600|19200|38400|57600|115200) + + + + + + + + Enable screen blank powersaving on VGA console + + + + + + + + diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py new file mode 100755 index 000000000..d1d9b7c70 --- /dev/null +++ b/src/conf_mode/system_console.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os + +from vyos.config import Config +from vyos.util import call +from vyos import ConfigError, airbag +airbag.enable() + +serial_getty_file = '/lib/systemd/system/serial-getty@.service' + +def get_config(): + conf = Config() + base = ['system', 'console'] + + if not conf.exists(base): + return None + + # retrieve configuration at once + console = conf.get_config_dict(base) + + # set default values + if 'device' in console.keys(): + for device in console['device'].keys(): + # no speed setting has been configured - use default value + if not 'speed' in console['device'][device].keys(): + tmp = { 'speed': '' } + if device.startswith('hvc'): + tmp['speed'] = 38400 + else: + tmp['speed'] = 115200 + + console['device'][device].update(tmp) + + return console + +def verify(console): + if not os.path.isfile(serial_getty_file): + raise ConfigError(f'Could not open: {serial_getty_file}') + + return None + +def generate(console): + base_dir = '/etc/systemd/system' + # Remove all serial-getty configuration files in advance + for root, dirs, files in os.walk(base_dir): + for basename in files: + if 'serial-getty' in basename: + call(f'systemctl stop {basename}') + os.unlink(os.path.join(root, basename)) + + # bail out early if serial device is not configured + if not console or 'device' not in console.keys(): + return None + + for device in console['device'].keys(): + serial_getty_device_file = f'{base_dir}/serial-getty@{device}.service' + serial_getty_wants_file = f'{base_dir}/getty.target.wants/serial-getty@{device}.service' + + with open(serial_getty_file, 'r') as f: + tmp = f.read() + tmp = tmp.replace('115200,38400,9600', str(console['device'][device]['speed'])) + + with open(serial_getty_device_file, 'w') as f: + f.write(tmp) + + # Reload systemd manager configuration + call('systemctl daemon-reload') + return None + +def apply(console): + # bail out early + if not console: + call( '/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/console 2>&1') + return None + + # Configure screen blank powersaving on VGA console + if 'powersave' in console.keys(): + call('/usr/bin/setterm -blank 15 -powersave powerdown -powerdown 60 -term linux /dev/console 2>&1') + else: + call( '/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/console 2>&1') + + # Start getty process on configured serial interfaces + for device in console['device'].keys(): + call(f'systemctl start serial-getty@{device}.service') + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) -- cgit v1.2.3 From 0ce5f5e7c2173b605125136dbfd20d10f5c32cac Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 8 Jun 2020 22:41:23 +0200 Subject: netconsole: T2561: use migrator to delete config nodes The current implementation only works once the system has been fully booted up and the config nodes have been process. So there is no "early" kernel debugging. It is started with priority 400 (after all network stuff) - thus it has a questionable at all for Kernel debugging. It would only make sense if the entire system is changed to supply the config stuff to the Kernel commandline and then send it to a dedicated MAC address target as network will be initialized late. As there are zero Phabricator tasks available and we do not know any user using this - the "feature" will be removed. --- src/migration-scripts/system/16-to-17 | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100755 src/migration-scripts/system/16-to-17 diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 new file mode 100755 index 000000000..34ce5a71d --- /dev/null +++ b/src/migration-scripts/system/16-to-17 @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# remove "system console netconsole" + +import os +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['system', 'console'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + # remove "system console netconsole" (T2561) + if config.exists(base + ['netconsole']): + config.delete(base + ['netconsole']) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) -- cgit v1.2.3 From 7b565f0bcc0dcfc7aa95cdbbb63264f5ba41456e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 8 Jun 2020 22:43:23 +0200 Subject: console: T2570: remove support for Hayes Modems Support for Hayes modems has been long gone (1.2.x) and nobody cared. It was removed in commit d582bbaf3 ("update console settings for systemd") of vyatta-cfg-system. So as there have been zero complaints - cleanup the CLI. --- src/migration-scripts/system/16-to-17 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 index 34ce5a71d..ca3b10f49 100755 --- a/src/migration-scripts/system/16-to-17 +++ b/src/migration-scripts/system/16-to-17 @@ -15,6 +15,7 @@ # along with this program. If not, see . # remove "system console netconsole" +# remove "system console device modem" import os import sys @@ -40,6 +41,12 @@ else: if config.exists(base + ['netconsole']): config.delete(base + ['netconsole']) + for device in config.list_nodes(base + ['device']): + dev_path = base + ['device', device] + # remove "system console device modem" (T2570) + if config.exists(dev_path + ['modem']): + config.delete(dev_path + ['modem']) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From bafa91b945ac77e2e1d000e356ca819bd5f87460 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 8 Jun 2020 22:45:04 +0200 Subject: console: T2529: migrate from ttyUSB device to new device in /dev/serial/by-bus During testing it was discovered that there is a well known problem (we had for ethernet interfaces) also in the serial port world. They will be enumerated and mapped to /dev/ttyUSBxxx differently from boot to boot. This is especially painful on my development APU4 board which also has a Sierra Wireless MC7710 LTE module installed. The serial port will toggle between ttyUSB2 and ttyUSB5 depending on the amount of serial port extenders attached (FT4232H). The shipped udev rule (/usr/lib/udev/rules.d/60-serial.rules) partly solves this by enumerating the devices into /dev/serial/by-id folder with their name and serial number - it's a very good idea but I've found that not all of the FT4232H dongles have a serial number programmed - this leads to the situation that when you plug in two cables with both having serial number 0 - only one device symlink will appear - the previous one is always overwritten by the latter one. Derive /usr/lib/udev/rules.d/60-serial.rules and create a /dev/serial/by-bus directory and group devices by attached USB root port. --- data/templates/getty/serial-getty.service.tmpl | 37 ++++++++++++++++ interface-definitions/system-console.xml.in | 14 +++--- src/conf_mode/system_console.py | 59 ++++++++++++-------------- src/migration-scripts/system/16-to-17 | 20 +++++++++ 4 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 data/templates/getty/serial-getty.service.tmpl diff --git a/data/templates/getty/serial-getty.service.tmpl b/data/templates/getty/serial-getty.service.tmpl new file mode 100644 index 000000000..0183eae7d --- /dev/null +++ b/data/templates/getty/serial-getty.service.tmpl @@ -0,0 +1,37 @@ +[Unit] +Description=Serial Getty on %I +Documentation=man:agetty(8) man:systemd-getty-generator(8) +Documentation=http://0pointer.de/blog/projects/serial-console.html +BindsTo=dev-%i.device +After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target +After=vyos-router.service + +# If additional gettys are spawned during boot then we should make +# sure that this is synchronized before getty.target, even though +# getty.target didn't actually pull it in. +Before=getty.target +IgnoreOnIsolate=yes + +# IgnoreOnIsolate causes issues with sulogin, if someone isolates +# rescue.target or starts rescue.service from multi-user.target or +# graphical.target. +Conflicts=rescue.service +Before=rescue.service + +[Service] +# The '-o' option value tells agetty to replace 'login' arguments with an +# option to preserve environment (-p), followed by '--' for safety, and then +# the entered username. +ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud {{ speed }} %I $TERM +Type=idle +Restart=always +UtmpIdentifier=%I +TTYPath=/dev/%I +TTYReset=yes +TTYVHangup=yes +KillMode=process +IgnoreSIGPIPE=no +SendSIGHUP=yes + +[Install] +WantedBy=getty.target diff --git a/interface-definitions/system-console.xml.in b/interface-definitions/system-console.xml.in index ccaaa51b2..71e63d0cb 100644 --- a/interface-definitions/system-console.xml.in +++ b/interface-definitions/system-console.xml.in @@ -11,20 +11,24 @@ Serial console device name + + + + ttySN - Serial device name + TTY device name, regular serial port - ttyUSBX - USB Serial device name + usbNbXpY + TTY device name, USB based - hvc0 + hvcN Xen console - ^(ttyS|ttyUSB|hvc)[0-9]+$ + ^(ttyS[0-9]+|hvc[0-9]+|usb[0-9]+b.*)$ diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index d1d9b7c70..a3e450a36 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -18,40 +18,46 @@ import os from vyos.config import Config from vyos.util import call +from vyos.template import render from vyos import ConfigError, airbag airbag.enable() -serial_getty_file = '/lib/systemd/system/serial-getty@.service' - def get_config(): conf = Config() base = ['system', 'console'] - if not conf.exists(base): - return None - # retrieve configuration at once console = conf.get_config_dict(base) - # set default values - if 'device' in console.keys(): - for device in console['device'].keys(): - # no speed setting has been configured - use default value - if not 'speed' in console['device'][device].keys(): - tmp = { 'speed': '' } - if device.startswith('hvc'): - tmp['speed'] = 38400 - else: - tmp['speed'] = 115200 + # bail out early if no serial console is configured + if 'device' not in console.keys(): + return console - console['device'][device].update(tmp) + # convert CLI values to system values + for device in console['device'].keys(): + # no speed setting has been configured - use default value + if not 'speed' in console['device'][device].keys(): + tmp = { 'speed': '' } + if device.startswith('hvc'): + tmp['speed'] = 38400 + else: + tmp['speed'] = 115200 + + console['device'][device].update(tmp) + + 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 + # amount of connected devices. We will resolve the fixed device name + # to its dynamic device file - and create a new dict entry for it. + # + # updating the dict must come as last step in the loop! + tmp = os.path.basename(os.readlink('/dev/serial/by-bus/usb0b1p1.0')) + console['device'][tmp] = console['device'].pop(device) return console def verify(console): - if not os.path.isfile(serial_getty_file): - raise ConfigError(f'Could not open: {serial_getty_file}') - return None def generate(console): @@ -63,20 +69,9 @@ def generate(console): call(f'systemctl stop {basename}') os.unlink(os.path.join(root, basename)) - # bail out early if serial device is not configured - if not console or 'device' not in console.keys(): - return None - for device in console['device'].keys(): - serial_getty_device_file = f'{base_dir}/serial-getty@{device}.service' - serial_getty_wants_file = f'{base_dir}/getty.target.wants/serial-getty@{device}.service' - - with open(serial_getty_file, 'r') as f: - tmp = f.read() - tmp = tmp.replace('115200,38400,9600', str(console['device'][device]['speed'])) - - with open(serial_getty_device_file, 'w') as f: - f.write(tmp) + config_file = base_dir + f'/serial-getty@{device}.service' + render(config_file, 'getty/serial-getty.service.tmpl', console['device'][device]) # Reload systemd manager configuration call('systemctl daemon-reload') diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 index ca3b10f49..981149d1b 100755 --- a/src/migration-scripts/system/16-to-17 +++ b/src/migration-scripts/system/16-to-17 @@ -47,6 +47,26 @@ else: if config.exists(dev_path + ['modem']): config.delete(dev_path + ['modem']) + # Only continue on USB based serial consoles + if not 'ttyUSB' in device: + continue + + # A serial console has been configured but it does no longer + # exist on the system - cleanup + if not os.path.exists(f'/dev/{device}'): + config.delete(dev_path) + continue + + # migrate from ttyUSB device to new device in /dev/serial/by-bus + for root, dirs, files in os.walk('/dev/serial/by-bus'): + for usb_device in files: + device_file = os.path.realpath(os.path.join(root, usb_device)) + # migrate to new USB device names (T2529) + if os.path.basename(device_file) == device: + config.copy(dev_path, base + ['device', usb_device]) + # Delete old USB node from config + config.delete(dev_path) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From ce7bf15a508beb29d59088c8b5b4ecaeb1694df7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 9 Jun 2020 20:38:46 +0200 Subject: console: T2569: only start serial console if device exists Only start console if it exists on the running system. If a user detaches a USB serial console and reboots - it should not fail! --- src/conf_mode/system_console.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index a3e450a36..6d60a9d4d 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -22,6 +22,8 @@ from vyos.template import render from vyos import ConfigError, airbag airbag.enable() +by_bus_dir = '/dev/serial/by-bus' + def get_config(): conf = Config() base = ['system', 'console'] @@ -50,10 +52,11 @@ def get_config(): # getty, but that name may change across reboots - depending on the # amount of connected devices. We will resolve the fixed device name # to its dynamic device file - and create a new dict entry for it. - # - # updating the dict must come as last step in the loop! - tmp = os.path.basename(os.readlink('/dev/serial/by-bus/usb0b1p1.0')) - console['device'][tmp] = console['device'].pop(device) + by_bus_device = f'{by_bus_dir}/{device}' + if os.path.isdir(by_bus_dir) and os.path.exists(by_bus_device): + tmp = os.path.basename(os.readlink(by_bus_device)) + # updating the dict must come as last step in the loop! + console['device'][tmp] = console['device'].pop(device) return console @@ -91,7 +94,10 @@ def apply(console): # Start getty process on configured serial interfaces for device in console['device'].keys(): - call(f'systemctl start serial-getty@{device}.service') + # Only start console if it exists on the running system. If a user + # detaches a USB serial console and reboots - it should not fail! + if os.path.exists(f'/dev/{device}'): + call(f'systemctl start serial-getty@{device}.service') return None -- cgit v1.2.3 From e45f8c9ccb7d192887375bfee9fc6357f4811654 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 9 Jun 2020 21:13:32 +0200 Subject: Debian: fix warning about undefined substitution variables warning: Depends field of package vyos-1x: substitution variable ${shlibs:Depends} used, but is not defined warning: Depends field of package vyos-1x-vmware: substitution variable ${shlibs:Depends} used, but is not defined Remove variables from dependency list as we have explicit non architecture dependend dependencies. --- debian/control | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/debian/control b/debian/control index 7c1555416..104a267ea 100644 --- a/debian/control +++ b/debian/control @@ -98,17 +98,13 @@ Depends: python3, salt-minion, vyos-utils, nftables (>= 0.9.3), - conntrack, - ${shlibs:Depends}, - ${misc:Depends} + conntrack Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything Package: vyos-1x-vmware Architecture: amd64 i386 Depends: - ${misc:Depends}, - ${shlibs:Depends}, vyos-1x, open-vm-tools Description: VyOS configuration scripts and data for VMware -- cgit v1.2.3 From d135e1b7b02b99b8b1f0a6232bc1c6e89abcdf4b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 11 Jun 2020 14:32:36 +0200 Subject: console: T2569: replicate console settings to grub.cfg --- src/conf_mode/system_console.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 6d60a9d4d..59a064b3a 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -15,7 +15,9 @@ # along with this program. If not, see . import os +import re +from fileinput import input as replace_in_file from vyos.config import Config from vyos.util import call from vyos.template import render @@ -78,6 +80,28 @@ def generate(console): # Reload systemd manager configuration call('systemctl daemon-reload') + + # GRUB + # For existing serial line change speed (if necessary) + # Only applys to ttyS0 + if 'ttyS0' not in console['device'].keys(): + return None + + speed = console['device']['ttyS0']['speed'] + grub_config = '/boot/grub/grub.cfg' + if not os.path.isfile(grub_config): + return None + + # stdin/stdout are redirected in replace_in_file(), thus print() is fine + p = re.compile(r'^(.* console=ttyS0),[0-9]+(.*)$') + for line in replace_in_file(grub_config, inplace=True): + if line.startswith('serial --unit'): + line = f'serial --unit=0 --speed={speed}\n' + elif p.match(line): + line = '{},{}{}\n'.format(p.search(line)[1], speed, p.search(line)[2]) + + print(line, end='') + return None def apply(console): -- cgit v1.2.3 From ef6f5d8054bb4e6a35260b86ebd845c8289fbaca Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 11 Jun 2020 14:47:08 +0200 Subject: console: T2569: run VGA console powersave on tty1 --- src/conf_mode/system_console.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 59a064b3a..0831232fb 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -105,16 +105,14 @@ def generate(console): return None def apply(console): - # bail out early + # reset screen blanking + call('/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/tty1 2>&1') if not console: - call( '/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/console 2>&1') return None - # Configure screen blank powersaving on VGA console if 'powersave' in console.keys(): - call('/usr/bin/setterm -blank 15 -powersave powerdown -powerdown 60 -term linux /dev/console 2>&1') - else: - call( '/usr/bin/setterm -blank 0 -powersave off -powerdown 0 -term linux /dev/console 2>&1') + # Configure screen blank powersaving on VGA console + call('/usr/bin/setterm -blank 15 -powersave powerdown -powerdown 60 -term linux /dev/tty1 2>&1') # Start getty process on configured serial interfaces for device in console['device'].keys(): -- cgit v1.2.3