dpkg-divert: override a file in a Debian package
I boot Linux in text mode, with all the boot-time messages showing. There are several reasons for this, but one is that I want to be able to see any errors that might arise — boot-time errors aren't otherwise shown to the user.
However, many Linux distros, including Debian and Ubuntu, clear the screen before showing a login prompt, making it impossible to read the last few messages or find any errors.
Some years back, I looked into why this was happening, and found the
answer in Stop
Clearing My God Damned Console. It comes down to a line in
getty@tty1.service: TTYVTDisallocate=yes
.
Change that to TTYVTDisallocate=no
and the terminal
will stop clearing before you log in.
The page suggests creating the directory
/etc/systemd/system/getty@.service.d, then creating a file
inside it, noclear.conf. But I never got that to work; maybe I
missed one of the steps, maybe it had to do with the difference between
/lib/systemd and /etc/systemd,
or maybe I was hit by one of systemd's periodic syntax changes.
The only way I got TTYVTDisallocate=no
was by modifying
/lib/systemd/system/getty@.service itself.
But that file is very frequently overwritten: any changes to it are erased every time there's an update to the systemd package. Systemd doesn't check, like some packages do, to see whether your version is different from the new version; it just overwrites the file silently. So my change would work for a few weeks, then I'd boot to a blank screen and realize my change had gotten overwritten again, and I had to go find my instructions to re-edit that file.
How could I stop dpkg from overwriting my changes?
At least on some filesystems, you can use chattr to make a
file unwriteable. I tried that for a while:
chattr +i /lib/systemd/system/getty@.service
It worked; but chattr is a blunt tool and I worried that dpkg might eventually overwrite it anyway, or else a package installation would fail because it couldn't write that file.
dpkg-divert
Eventually I found a better way: dpkg-divert.
This tool lets you tell dpkg that when it wants to install a file with pathname X, instead it should install to path Y. In this case:
dpkg-divert --divert /lib/systemd/system/getty@.service.new --no-rename /lib/systemd/system/getty@.service
That tells dpkg that when a package says to install a new version of /lib/systemd/system/getty@.service, instead, put it in /lib/systemd/system/getty@.service.new and leave the existing file alone. Note that the arguments are the opposite of what you'd expect: the new filename comes first, the expected filename last.
You can list your system's current diversions:
$ dpkg-divert --list [ ... ] local diversion of /lib/systemd/system/getty@.service to /lib/systemd/system/getty@.service.new [ ... ]You'll probably find there are already several in place.
Remove a diversion thus:
dpkg-divert --remove --no-rename /lib/systemd/system/getty@.service
I confess to being shaky about the --no-rename part: I found the documentation very unclear on that (why would it be renaming anything when removing a diversion?) but dpkg-divert requires that you specify either --rename or --no-rename even on a --remove. As for creating a diversion, I created my diversion with --rename, but from what I can glean from the documentation, copying the file rather than renaming it probably makes more sense, so --no-rename is probably better (and besides, --rename will bail if the file already exists). When removing a diversion, if you don't specify one or the other you get a long warning message saying you should specify one or the other explicitly, but I'm not sure why it would make any difference at all in the --remove case.
I'd be interested to hear from any readers who understand the purpose of --rename, or what difference it makes when removing a diversion. I couldn't find a clear explanation.
Checking Your Work
Of course, I was curious as to whether my diversion was really working. So I added this to my .zlogin:
if [[ -e /lib/systemd/system/getty@.service.new ]]; then echo '*****************' echo "Systemd tried to install a new /lib/systemd/system/getty@.service" echo "diff /lib/systemd/system/getty@.service /lib/systemd/system/getty@.service.new" echo '*****************' fi
Now every couple of weeks I see that message upon logging in. Then I run the indicated diff command to see that invariably, the file hasn't changed at all and the only difference is that it would have flipped back to TTYVTDisallocate=yes. That means it' safe to remove the new /lib/systemd/system/getty@.service.new.
[ 19:08 Oct 10, 2022 More linux | permalink to this entry | ]