Getting a fixed baud rate on your serial ports for logins under systemd

March 22, 2022

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.


Comments on this page:

This seems like a symptom of systemd’s “override” feature, which imho is over engineered. You’re writing a delta file that is applied over the top of the vendor supplied service file. I’ve always found this awkward. Firstly, getting an empty file after “systemctl edit” is awkward because I tend to want to refer to the source when editing something. Rather than use override files, which seems to promise a loose coupling between vendor unit and your override, but deliver a tight coupling, I wish the work flow simply copied the whole vendor unit file to the /etc location that would totally override it, and then edit away at that. The drawback is you don’t pick up any changes the vendor unit file gets with package updates. But who really knows if those would be compatible with your delta anyway?

By Simon Deziel at 2022-03-23 14:08:22:

@Jonathan:

Firstly, getting an empty file after “systemctl edit” is awkward because I tend to want to refer to the source when editing something.

Newer systemd version will open an editor with the commented out original file you are about to override.

Rather than use override files, [...] simply copied the whole vendor unit file to the /etc location that would totally override it, and then edit away at that.

`systemctl edit --full` is exactly for that.

The drawback is you don’t pick up any changes the vendor unit file gets with package updates. But who really knows if those would be compatible with your delta anyway?

Partial overrides can be useful for site specific changes like hardening, or ordering requirements for example.

Written on 22 March 2022.
« The modern trend of variable DNS results and its effects on troubleshooting
Document your mistakes and then try to block them in the future »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Tue Mar 22 22:02:47 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.