An annoyance with Debian postinstall scripts during package upgrades

January 6, 2022

Both RPMs and Debian packages have broadly the same high level features, which isn't surprising because they're facing (and solving) the same problems. For example, both have postinstall scripts. However, the details and the social customs that result are different between the two, and so every so often I find something that irritates me about one or the other (usually Debian). Today's irritation is in postinstall scripts.

In RPM, postinstall scripts can tell if they're being run during a package upgrade instead of during a package's first installation. In Fedora, as far as I know the social custom for postinst scripts is that they should detect upgrades and skip re-doing work and re-running commands that are only applicable for the first install. This is true even if the commands theoretically do nothing if re-run.

In Debian packages, as far as I can tell, postinstall scripts don't get told whether this is a first install or an upgrade. Certainly the social custom for package postinstall scripts is that they don't try to tell; whether they're installing or upgrading, they'll (re-)run the same commands. It's up to your system and the commands to do nothing if they're re-run.

Unfortunately this can create a situation where a Debian or Ubuntu package is properly installed and working fine, but something changes in the system so that the commands in its postinstall script will be unhappy if they're rerun. If a package upgrade is released and you apply it, your system will kind of blow up. The package's postinst script will get run, it will repeat all of the actions that are only relevant at install time, a command will find something it doesn't like, and now you have postinst script failure and a problem:

dpkg: error processing package systemd (--configure):
 installed systemd package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 systemd
E: Sub-process /usr/bin/dpkg returned an error code (1) 

This will give you one or more packages in an odd state and you're going to have to fix that to make your package system happy, despite the fact that the package itself is correctly installed and fully functional. And everything would have been fine if the postinst script could have easily told an install from an upgrade and not done the install-time work at upgrade time too.

PS: If you look in /var/lib/dpkg/info, you'll probably discover that you have more postinst scripts than you might have thought and they do more than you expect. It's nice that the Debian package format makes package scripts more visible and accessible with standard Unix tools than the RPM format does; in RPM, all of this information is locked up inside a database and has to be pried out with magic.


Comments on this page:

By Tilman Baumann at 2022-01-07 04:57:53:

I lack the resources to quickly confirm it completely. But from memories and also the document you posted https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html#summary-of-ways-maintainer-scripts-are-called you see the old version is passed as an argument to the scripts.

I suppose you can use this to infer if this is an upgrade or not.

I built a lot of debian package in a previous life. Complex postinst that had to take care of special version upgrades usually had a long list of version-range-match if statements to take care of special upgrade cases.

By Ben Hutchings at 2022-01-07 22:05:17:

The postinst script is given the last configured version (or '' if there was none, I think), but that might be a version that was removed leaving configuration files around. The postinst script is supposed to use this to determine whether (and which) automated changes should be made to configuration files.

The preinst script is told whether this is an install or upgrade, and it can use a flag file to pass that on to the postinst. But this isn't common practice.

By Chris H at 2022-01-11 03:47:13:

Treating fresh installs as different from upgrades is IMO wrong. Fresh installs really are a special case of upgrades, just from a "really old version".

This should become clear if you think about the evolution of a software package. In each new version, new stuff can appear. This new stuff might need "install-time scripts", like, say, new users.

Now each upgrade is a "fresh install" for some part of the package, and making a difference between installs and upgrades of the entire package has become meaningless.

One could certainly use package version comparisons to determine which code should run only on "fresh install of this part" and "upgrades", but in the end its IMO better to declare "X should be the desired state" and have the scripts do that. adduser --system is designed to be run multiple times with no problem (but I see in your follow-up article there is some additional complication...).

Written on 06 January 2022.
« Some things about Dovecot, its index files, and the IMAP SELECT command
In practice, Debian (and Ubuntu) have fixed minimum system UIDs and GIDs »

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

Last modified: Thu Jan 6 22:14:01 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.