Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy the console= kernel arguments from the original system #2961

Merged
merged 23 commits into from Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 67 additions & 16 deletions usr/share/rear/conf/default.conf
Expand Up @@ -3289,39 +3289,90 @@ SIMPLIFY_TEAMING=no

####
# Serial console support for the ReaR rescue/recovery system:
# By default serial consoles get enabled if serial devices are found and
# then matching kernel command line parameters like 'console=ttyS0,9600 console=ttyS1,9600'
# are set when booting the rescue/recovery system (see KERNEL_CMDLINE above).
# IA64 platforms require it and people need it when no VGA console is available.
# Specify 'yes' or 'no' or leave it empty to use ReaR's automatism:
# By default there is serial console support in the ReaR recovery system
# when 'getty' or 'agetty' and 'stty' can be found,
# otherwise USE_SERIAL_CONSOLE is automatically set to 'no'.
jsmeix marked this conversation as resolved.
Show resolved Hide resolved
# When 'getty' or 'agetty' and 'stty' can be found
# and there is a 'console=...' option in /proc/cmdline,
# then USE_SERIAL_CONSOLE is automatically set to 'yes'.
jsmeix marked this conversation as resolved.
Show resolved Hide resolved
# With USE_SERIAL_CONSOLE="no" no serial console gets set up,
# neither for the recovery system kernel nor for the recovery system bootloader.
# With USE_SERIAL_CONSOLE="yes" plus appropriate SERIAL_CONSOLE_DEVICE... settings
# serial consoles can be specified for the recovery system kernel and bootloader
# for example when there is no 'console=...' option in /proc/cmdline
# or when serial consoles for the recovery system kernel and bootloader
# should differ from what 'console=...' options in /proc/cmdline tell.
# By default (when empty) the automatism further described below is used:
USE_SERIAL_CONSOLE=
# If you do not want to use all serial devices found as console you can specify the ones to use
# e.g. SERIAL_CONSOLE_DEVICES="/dev/ttyS0 /dev/ttyS1" provided USE_SERIAL_CONSOLE is turned on.
# By default (when empty) all serial devices found are used:
#
# Devices to be used in general for serial console setup
# unless explicitly specified via SERIAL_CONSOLE_DEVICES_KERNEL
# or SERIAL_CONSOLE_DEVICE_SYSLINUX or SERIAL_CONSOLE_DEVICE_GRUB:
# E.g. SERIAL_CONSOLE_DEVICES="/dev/ttyS0 /dev/ttyS1"
SERIAL_CONSOLE_DEVICES=
# Serial consoles for the kernel of the recovery system:
#
# Serial consoles for the kernel of the recovery system
# provided USE_SERIAL_CONSOLE is not 'no':
# SERIAL_CONSOLE_DEVICES_KERNEL can be device nodes like "/dev/ttyS0 /dev/ttyS1"
# or 'console=...' kernel parameters like "console=ttyS1,9600 console=tty0" or both like
# SERIAL_CONSOLE_DEVICES_KERNEL="/dev/ttyS0 console=ttyS1,9600 console=tty0"
# provided USE_SERIAL_CONSOLE is turned on.
# By default (when empty) the SERIAL_CONSOLE_DEVICES are used for the kernel:
# When SERIAL_CONSOLE_DEVICES_KERNEL is empty but SERIAL_CONSOLE_DEVICES is specified
# then the specified SERIAL_CONSOLE_DEVICES are used for the kernel.
# By default (when SERIAL_CONSOLE_DEVICES_KERNEL and SERIAL_CONSOLE_DEVICES are empty)
# serial consoles get enabled for the recovery system kernel via COPY_KERNEL_PARAMETERS
# for all 'console=...' options that are found in /proc/cmdline.
SERIAL_CONSOLE_DEVICES_KERNEL=
# Serial console for SYSLINUX/EXTLINUX when it is used as bootloader for the recovery system:
#
# Serial console for SYSLINUX/EXTLINUX when it is used as bootloader for the recovery system
# provided USE_SERIAL_CONSOLE is 'yes' (automatically with 'console=...' in /proc/cmdline):
# SYSLINUX supports only one serial device (because the last SYSLINUX 'serial' directive wins).
# The right /dev/ttyS* can be specified like SERIAL_CONSOLE_DEVICE_SYSLINUX="/dev/ttyS0"
# or a whole SYSLINUX 'serial' directive can be specified e.g. for /dev/ttyS1 like
# SERIAL_CONSOLE_DEVICE_SYSLINUX="serial 1 9600"
# provided USE_SERIAL_CONSOLE is turned on.
# By default (when empty) the first one of SERIAL_CONSOLE_DEVICES is used for SYSLINUX:
# When SERIAL_CONSOLE_DEVICE_SYSLINUX is empty but SERIAL_CONSOLE_DEVICES is specified
# then the first one of SERIAL_CONSOLE_DEVICES is used for SYSLINUX.
# By default (when SERIAL_CONSOLE_DEVICE_SYSLINUX and SERIAL_CONSOLE_DEVICES are empty)
# the devices of the 'console=...' options in /proc/cmdline
# that exist as /dev/ttyS* or /dev/hvsi* character device nodes are used
# (which excludes /dev/tty0 when there is 'console=tty0' in /proc/cmdline).
SERIAL_CONSOLE_DEVICE_SYSLINUX=
# Serial console for GRUB when it is used as bootloader for the recovery system:
#
# Serial console for GRUB when it is used as bootloader for the recovery system
# provided USE_SERIAL_CONSOLE is 'yes' (automatically with 'console=...' in /proc/cmdline):
# GRUB supports only one serial device (because the last GRUB 'serial' command wins).
# The right /dev/ttyS* can be specified like SERIAL_CONSOLE_DEVICE_GRUB="/dev/ttyS0"
# or a whole GRUB 'serial' command can be specified e.g. for /dev/ttyS1 like
# SERIAL_CONSOLE_DEVICE_GRUB="serial --unit=1 --speed=9600"
# provided USE_SERIAL_CONSOLE is turned on.
# By default (when empty) the first one of SERIAL_CONSOLE_DEVICES is used for GRUB:
# When SERIAL_CONSOLE_DEVICE_GRUB is empty but SERIAL_CONSOLE_DEVICES is specified
# then the first one of SERIAL_CONSOLE_DEVICES is used for GRUB.
# By default (when SERIAL_CONSOLE_DEVICE_GRUB and SERIAL_CONSOLE_DEVICES are empty)
# the devices of the 'console=...' options in /proc/cmdline
# that exist as /dev/ttyS* or /dev/hvsi* character device nodes are used
# (which excludes /dev/tty0 when there is 'console=tty0' in /proc/cmdline).
SERIAL_CONSOLE_DEVICE_GRUB=
#
# Examples
# (provided USE_SERIAL_CONSOLE is not 'no'
# and 'getty' or 'agetty' and 'stty' can be found):
#
# Default behaviour
# when there is no 'console=...' option in /proc/cmdline
# then no serial console is set up for the recovery system.
#
# Default behaviour when there are for example
# 'console=ttyS0,9600' and 'console=tty0' in /proc/cmdline
# then 'console=ttyS0,9600' and 'console=tty0'
# are used for the recovery system kernel
# and only /dev/ttyS0 is used as serial console
# for the recovery system bootloader (SYSLINUX or GRUB).
#
# On a headless machine without VGA card but with serial device /dev/ttyS0
# the kernel would choose /dev/ttyS0 as its console automatically
# so no 'console=...' kernel option needs to be used and then the automatism described above
# would not set up a console for the recovery system (in particular not for the bootloader).
# In this case USE_SERIAL_CONSOLE="yes" and appropriate SERIAL_CONSOLE_DEVICE... settings
# are needed to manually specify the right console setup for the recovery system.
####

####
Expand Down
110 changes: 33 additions & 77 deletions usr/share/rear/lib/serial-functions.sh
Expand Up @@ -6,29 +6,34 @@ function get_serial_console_devices () {
echo $SERIAL_CONSOLE_DEVICES
return 0
fi
# Test if there is /dev/ttyS[0-9]* or /dev/hvsi[0-9]*
# because when there is neither /dev/ttyS[0-9]* nor /dev/hvsi[0-9]*
# the ls command below would become plain 'ls' because of 'nullglob'
# cf. "Beware of the emptiness" in https://github.com/rear/rear/wiki/Coding-Style
# see https://github.com/rear/rear/issues/2914#issuecomment-1396659184
# and return 0 because it is no error when no serial device node exists
test "$( echo -n /dev/ttyS[0-9]* /dev/hvsi[0-9]* )" || return 0
# Use plain 'sort' which results /dev/ttyS0 /dev/ttyS1 /dev/ttyS10 ... /dev/ttyS19 /dev/ttyS2 /dev/ttyS20 ...
# to get at least /dev/ttyS0 and /dev/ttyS1 before the other /dev/ttyS* devices because
# we cannot use "sort -V" which would result /dev/ttyS0 /dev/ttyS1 ... /dev/ttyS9 /dev/ttyS10 ...
# because in older Linux distributions 'sort' does not support '-V' e.g. SLES10 with GNU coreutils 5.93
# (SLES11 with GNU coreutils 8.12 supports 'sort -V') but if 'sort' fails there is no output at all
# cf. "Maintain backward compatibility" in https://github.com/rear/rear/wiki/Coding-Style
# Furthermore 'sort' results that /dev/hvsi* devices appear before /dev/ttyS* devices
# so the create_grub2_serial_entry function in lib/bootloader-functions.sh
# which uses by default the first one and skips the rest will result that
# the first /dev/hvsi* device becomes used for the GRUB serial console by default
# which looks right because /dev/hvsi* devices should exist only on systems
# that have the HVSI driver loaded (a console driver for IBM's p5 servers)
# cf. https://lwn.net/Articles/98442/
# and it seems right that when special console drivers are loaded
# then their devices should be preferred by default:
ls /dev/ttyS[0-9]* /dev/hvsi[0-9]* | sort
# Scan the kernel command line of the currently running original system
# for 'console=<device>[,<options>]' settings e.g. 'console=ttyS1,9600n8 ... console=ttyS3 ... console=tty0'
# and extract the specified serial device nodes e.g. ttyS1 -> /dev/ttyS1 ... ttyS3 -> /dev/ttyS3
local kernel_option console_option_value console_option_device
for kernel_option in $( cat /proc/cmdline ) ; do
# Continue with next kernel option when the option name (part before leftmost "=") is not 'console':
test "${kernel_option%%=*}" = "console" || continue
# Get the console option value (part after leftmost "=") e.g. 'ttyS1,9600n8' 'ttyS3' 'tty0'
console_option_value="${kernel_option#*=}"
# Get the console option device (part before leftmost optional ',' separator) e.g. 'ttyS1' 'ttyS3' 'tty0'
console_option_device="${console_option_value%%,*}"
# Continue with next kernel option when the current console option device is no serial device (exclude 'tty0').
# The special /dev/hvsi* devices should exist only on systems that have the HVSI driver loaded
# (a console driver for IBM's p5 servers) cf. https://lwn.net/Articles/98442/
[[ $console_option_device == ttyS* ]] || [[ $console_option_device == hvsi* ]] || continue
# Test that the matching serial device node e.g. ttyS1 -> /dev/ttyS1 and ttyS3 -> /dev/ttyS3' exists
# to avoid that this automated serial console setup may not work in the ReaR recovery system
# when serial device nodes get specified for the recovery system that do not exist
# in the currently running original system because the default assumption is
# that the replacement system has same hardware as the original system,
# cf. https://github.com/rear/rear/pull/2749#issuecomment-1196650631
# (if needed the user can specify what he wants via SERIAL_CONSOLE_DEVICES, see above):
if ! test -c "/dev/$console_option_device" ; then
LogPrintError "Found '$kernel_option' in /proc/cmdline but '/dev/$console_option_device' is no character device"
continue
fi
echo /dev/$console_option_device
done
}

# Get the serial device speed for those device nodes that belong to actual serial devices.
Expand All @@ -39,64 +44,15 @@ function get_serial_device_speed () {
# Run it in a subshell so that 'set -o pipefail' does not affect the current shell and
# it can run in a subshell because the caller of this function only needs its stdout
# cf. the function get_root_disk_UUID in lib/bootloader-functions.sh
# so when stty fails the get_serial_device_speed return code is the stty exit code and not the awk exit code
# therefore one can call get_serial_device_speed with error checking for example like
# so when stty fails the get_serial_device_speed return code is the stty exit code and not the awk exit code.
# Therefore one can call get_serial_device_speed with error checking for example like
# speed=$( get_serial_device_speed $serial_device ) && COMMAND_WITH_speed || COMMAND_WITHOUT_speed
# because the return code of variable=$( PIPE ) is the return code of the pipe,
# cf. how get_serial_device_speed is called in cmdline_add_console below.
# because the return code of variable=$( PIPE ) is the return code of the pipe
# cf. how get_serial_device_speed is called in lib/bootloader-functions.sh
# and output/USB/Linux-i386/300_create_extlinux.sh
# Suppress stty stderr output because for most /dev/ttyS* device nodes the result is
# stty: /dev/ttyS...: Input/output error
# when the device node does not belong to an actual serial device (i.e. to real serial hardware)
# so get_serial_device_speed is also used to get those device nodes that belong to real serial devices:
( set -o pipefail ; stty -F $devnode 2>/dev/null | awk '/^speed / { print $2 }' )
}

# Add serial console to kernel cmdline:
function cmdline_add_console {
# Nothing to do when using serial console is not wanted:
is_true "$USE_SERIAL_CONSOLE" || return 0

# Strip existing 'console=...' kernel cmd parameters:
local param cmdline=""
for param in $KERNEL_CMDLINE ; do
case "$param" in
(console=*) ;;
(*) cmdline+=" $param";;
esac
done

# Add serial console config to kernel cmd line:
local devnode speed=""
if test "$SERIAL_CONSOLE_DEVICES_KERNEL" ; then
# When the user has specified SERIAL_CONSOLE_DEVICES_KERNEL use only that (no automatisms):
for devnode in $SERIAL_CONSOLE_DEVICES_KERNEL ; do
# devnode can be a character device node like "/dev/ttyS0" or "/dev/lp0" or "/dev/ttyUSB0"
# cf. https://www.kernel.org/doc/html/latest/admin-guide/serial-console.html
# or devnode can be a 'console=...' kernel cmd parameter like "console=ttyS1,9600"
if test -c "$devnode" ; then
if speed=$( get_serial_device_speed $devnode ) ; then
cmdline+=" console=${devnode##/dev/},$speed"
else
cmdline+=" console=${devnode##/dev/}"
fi
else
# When devnode is a 'console=...' kernel cmd parameter use it as specified:
cmdline+=" $devnode"
fi
done
else
local real_consoles=""
for devnode in $( get_serial_console_devices ) ; do
# Only add for those device nodes that belong to actual serial devices:
speed=$( get_serial_device_speed $devnode ) && real_consoles+=" console=${devnode##/dev/},$speed"
done
cmdline+=" $real_consoles"

# Add fallback console if no real serial device was found:
test "$real_consoles" || cmdline+=" console=tty0"
fi

# Have a trailing space to be on the safe side
# so that more kernel cmd parameters could be "just appended" by other scripts:
echo "$cmdline "
}