2022-03-22
Getting a fixed baud rate on your serial ports for logins under systemd
Many servers have support for serial consoles and in general for serial ports, either physical ones or virtual ones provided by things like IPMI Serial over LAN, and Linux (still) supports logins over them. Systemd makes life very convenient for any serial consoles (also) you have; if you configure the kernel with one or more kernel serial consoles via 'console=' arguments, systemd will automatically start a getty on it (as covered in Gettys on Serial Consoles (and Elsewhere) and the manual page for systemd-getty-generator).
(We no longer use serial consoles as /dev/console
, where things like systemd messages
go. But we still send kernel messages to them and support logging
in to machines over them, via our serial console server.)
The standard systemd 'serial-console@.service' unit uses agetty(8) with a variety
of baud rate options, specifying '--keep-baud 115200,57600,38400,9600
'.
As covered by the manual page, this means agetty will start out
with whatever baud rate the serial port is at and then cycle through
the baud rates with a serial BREAK.
On our kernel serial consoles, this appears to reliably keep agetty
at the kernel's baud rate (which we normally set to 115200 these days).
Unfortunately, on other serial ports this can result in the baud rate
winding up at 9600 baud. When the serial port is a virtual IPMI serial
over LAN port, sometimes this works (although slowly) and sometimes it
doesn't work at all.
To set a particular serial port to a fixed baud rate (the baud rate
you'd like and your IPMI expects), you need an override for
'serial-getty@ttyS<X>.service', which you can conveniently get with
'systemctl edit ...'. My first naive version just contained a
[Service]
section with a new ExecStart
for the revised agetty
command line. Unfortunately systemd objects to this, complaining
that you can't have two ExecStart lines in one .service unit of
this type. To fix this, we need to null out ExecStart in our drop-in
file, for an end result of:
[Service] ExecStart= ExecStart=-/sbin/agetty -o '-p -- \\u' 115200 %I $TERM
(Assuming that you want 115200 as your baud, which you probably do.)
The trick of clearing out (resetting) ExecStart
is covered in an
example in the manual page for systemd.unit
(as part of example 2). It may be covered elsewhere, but if so I didn't
spot it. In my opinion this should be better documented
(I believe I found out how to do this clearing from a StackOverflow answer, although I can't find the answer right now.)
PS: I don't know what's happening with additional serial ports to
drop them to 9600 baud so regularly. Since I can do 'stty -a
</dev/ttyS0; stty 115200 </dev/ttyS0; stty -a </dev/ttyS0
' and
have the serial port stick at 115200 baud (on a machine with no
getty on ttyS0), it's not as simple as the kernel resetting serial
ports to 9600 baud when they're closed. Maybe something makes
agetty think that the serial port should drop to 9600 baud as soon
as agetty starts talking to it. Since all of this was with IPMI
virtualized ports, perhaps agetty sees a whole cascade of BREAK
signals when it starts up with nothing connected to the virtual
serial port on the other side.