UP | HOME
Maintainer scripts

Maintainer scripts

Table of Contents

Introduction

This document is meant to clarify the interaction between maintainer scripts and dpkg, and examines the state changes for a package when a user interacts with the packaging system. The dynamic interactions between the packaging system and the package's maintainer scripts are described formally using UML diagrams1. This document does not attempt to describe what the maintainer scripts can or can not do, concentrating instead mostly one the packaging system interface. It also provides a call graph of the maintainer script.

This document is meant to be informative, not normative, at this point, and is presented here mostly since the maintainer scripts interaction section of policy is one of the more opaque segments. However, it also is trying to formally define the packaging system interface formally, and is meant to become normative at some point in the future, once it has buy in from the interested parties and has been checked for correctness.

This document was inspired by Margarita Manterola's excellent treatise on maintainer script invocations and return values2.

Package States and Flags

dpkg uses the following package states internally about all known packages

Apart from these states, dpkg may also flag a package on a system. Currently, the only flag available is:

  • reinst-required flag : A package marked reinst-required is broken and requires reinstallation. These packages cannot be removed, unless forced with option --force-remove-reinstreq.

Common Use Cases

To start with, we consider the most common use cases related to a user's interaction with the packaging system:

  • Install: Starting with a package which is not installed on the system, the user asks the packaging system to install a package. If all goes well, the end result is that the package is installed on the system.
  • Remove: Starting with a package that is currently installed on the system, the user asks the packaging system to remove it. If all goes well, only configuration files shall remain on the system at the end.
  • Purge: Starting with the state when only configuration files remain on the system, the user asks the package system to remove even the configuration files. If all goes well, the package will be fully un-installed.
  • Starting with a package installed on the system, the user asks the system to fully uninstall the package. This is not an independent use case, it is just the Remove followed by a Purge, and we do not consider it separately below.
  • Reinstall: Starting with just the configuration files remaining on the system, the user asks the packaging system to install the package again (potentially newer version of the package).
  • Upgrade: Starting with a version of the package installed on the system, the user asks the packaging system to install a newer version of the package. (A variation is the case where the old and the new version of the package are the same, but it follows the same process).

It is interesting to note that there are only three states a package may be in when the user initiates the action (Not-Installed state, Config-Files state, and Fully Installed state). These states are also successful terminal state, and there are only three failure terminal states (Half-Installed state, Config-Files state, and Unpacked state).

In the treatment below, we do not discuss details of failure modes, we just detail what happens when a maintainer script encounters an error. We also do not go into the details of conflicts handling during upgrades and installations, since that clutters up the diagram to the point of uselessness. In lieu of that. we present a standalone section explaining the handling of conflicting packages at the end.

Install a previously unknown package

This is the case where a package is not currently installed on the system, and the user asks the packaging system to install version V1. Installing packages is the most basic, and the most critical, feature of a packaging system. This is the bread and butter of a package management system.

Installation consists of the following steps:

  1. Extract the control files of the new package.
  2. If another version of the same package was installed before the new installation, execute prerm script of the old package.
  3. Run preinst script, if provided by the package.
  4. Unpack the new files, and at the same time back up the old files, so that if something goes wrong, they can be restored. No configuration is done at this point.
  5. If another version of the same package was installed before the new installation, execute the postrm script of the old package. Note that this script is executed after the preinst script of the new package, because new files are written at the same time old files are removed.
  6. Configure the package. Configuring consists of the following steps
    1. Unpack the conffiles, and at the same time back up the old conffiles, so that they can be restored if something goes wrong.
    2. Run postinst script, if provided by the package.

State diagram

The state diagram reflects the simplicity of the operation: when the user asks for a package to be installed, it may either be not installed at all, end up in the Half-Installed state (with the option of being installed later at user request), face a configuration failure (Config-Files state), or end up being properly installed (Installed state). Only one of the four terminal stages is a success condition.

State diagram:Install

State transition diagram for package installation

Collaboration diagram

The collaboration diagram reveals that dpkg calls the maintainer scripts preinst and postinst during normal operations, and the postrm during error unwind. Indeed, the postinst may be called twice, as we shall see in the activity diagram below.

State diagram:Install

Collaboration diagram for package installation

Activity diagram

The green ellipses are the start states a package may be in, the blue labels represent user actions. The maintainer scripts are represented by decision boxes, since usually how the process flows depends on whether the maintainer scripts succeeds or fails. Terminal states are octagons. The colors green and red denote success and failure, respectively.

For the most part, this is a straightforward operation, were it not for the fact that dpkg has to deal with conflicting packages and their dependents, and also the whole trigger activation process in the late installation processing. Remove the conflict handling, and triggers, the activity diagram is as simple as the collaboration diagram leaves us to believe. The deconfiguration and removal of conflicting packages and their dependent packages is detailed later in this document.

State diagram:Install

Activity diagram for package installation

Remove and Purge an installed package

This is the reverse operation from above: starting with a package in the Fully Installed state, remove the package, and then, if asked explicitly, purge the configuration files as well. This too is in the ball park of a bread-and-butter operation for a packaging system.

If purge is not called, it may avoid having to reconfigure the package if it is reinstalled later. purge removes everything, including conffiles 5. Removing of a package consists of the following steps:

  1. Run prerm script
  2. Remove the installed files
  3. Run postrm script

State diagram

This is a two stage operation. Staring from an Fully Installed state, remove moves the package to the Config-Files state, and from there purge moves it to the Not-Installed state. So, for these two operations, we have two success terminal states, and four failure mode states.

State diagram:Remove

State transition diagram for package removal and purge

Collaboration diagram

In the non failure mode, this is a simple operation. dpkg calls the prerm, postrm, and purge. The postinst scripts are only called during error-unwind, usually the postinst of the package being removed is called, unless prerm failed because of a conflicting package, in which case the postinst of the conflicting package is called.

State diagram:Install

Collaboration diagram for package removal

Activity Diagram

The only complication in this process happens if the prerm fails, because the actions taken differ based on whether the prerm failed due to a conflicting package. Interestingly, this activity diagram involves all three states in which user actions take place (Fully Installed state, Config-Files state, and Not-Installed state).

State diagram:Remove

Activity diagram for package removal and purge

Reinstall a package

This is the case where configuration files from a previous installation still exist on the system, and the user asks the packaging system to install a (potentially different) version of the package. To most users, this case is mostly indistinguishable from a straight install, and mostly looks the same on analysis.

State Diagram

This is mostly identical to the case of installation, though failure of the preinst script, the terminal failure state is the Config-Files state, instead of Not-Installed state 6.

State diagram:Reinstall

State transition diagram for package reinstallation

Collaboration Diagram

The collaboration diagram is identical to that of a straight installation. Indeed, apart from the early stages the old configurations files exist (until the new package is unpacked), the interactions between the packaging system and the maintainer scripts is unchanged.

State diagram:Reinstall

Collaboration diagram for package reinstallation

As with the collaboration diagram, the activity diagram is indistinguishable from that of a straight install,

Activity Diagram

State diagram:Reinstall

Activity diagram for package reinstallation

Upgrade a package to a new version

This is, perhaps unsurprisingly, the most complex operation of the ones we have so far considered. The sequence of activity in the trigger handling sequence is affected by external events (trigger activation and processing), though a package postinst script may activate a new trigger, and participates in handling any pending action requests in other packages it has expressed an interest in.

State Diagram

One of ways the complexity of the operation is expressed is that the number of failure mode terminal states is five, with only a single success terminal state. This is the only state transition that may end with a package version being abandoned in the Unpacked state.

State diagram:Upgrade

State transition diagram for package upgrades

Collaboration diagram

In the normal operation mode, only the old prerm, the new preinst, the old postrm, and the new postinst are called, as can be logically expected. However, the various failure modes and error unwind procedures add a further five maintainer scripts into the mix; this operation, thus, may involve no less than nine different maintainer scripts, some called more than once.

State diagram:Upgrade

Collaboration diagram for package upgrade

Activity Diagram

This is the most complex activity diagram we have seen so far, and the flurry of activity related to triggers around the post-installation stage adds to the complexity of the process. The iterative and asynchronous nature of the whole triggering process makes depiction of the process in a UML activity diagram cumbersome. Had we expanded the deconfiguration and removal of conflicting packages and their dependents into this activity diagram it would have been incomprehensible.

State diagram:Upgrade

Activity diagram for package upgrades

Handling Conflicts

Here we present a short explanation of what happens when a package is being installed or upgraded, and how conflicting packages and their dependencies are handled. This happens in two stages: first, we deconfigure any conflicting packages, or packages impacted by breaks.

Deconfigure packages

Before the package that is going to be installed or upgraded has its files unpacked, we deconfigure any packages that are in conflict (since conflicting packages can not have files on disk at the same time. We also deconfigure all packges that depend on the conflicting packages, and mark all these for removal.

State Diagram

This state diagram refers to the packages that conflict with the package which is being installed, and the packages that depend on the conflicting packages. It is a relatively simple transition; normally, the package is deconfigured, and if that fails, the package and/or one of its dependent packages may end up in the Half-Installed state.

State diagram:Deconfigure

State diagram for package deconfiguration

Collaboration Diagram

During deconfiguration of a package, normally only the prerm is called, and if it fails, the postinst is called to unwind the error. Since all conflicting packages, as well as their dependents, are deconfigured, the collaboration diagram becomes clear.

State diagram:Deconfigure

Collaboration diagram for package deconfiguration

Activity Diagram

The activity diagram is essentially two identical sections, the top half details deconfiguring conflicting packages, and the bottom half deals with any packages that depend on the conflicting packages. It looks more complicated than it is because of the loops. All this happens before the package being installed is unpacked.

Also, while deconfiguring packages, the deconfigured package is marked as needing to be removed once the unpack phase finishes without error.

State diagram:Deconfigure

Activity diagram for package deconfiguration

Remove Conflicting Packages

After the package to be installed has been unpacked, we remove any packages we have previously deconfigured and marked for removal. The operation is identical to the simple removal (no purge activity) detailed above, and the diagrams are the same, and are not being reproduced here. The only interesting thing here is that we loop over all the packages marked as deconfigured, and remove each in turn.

A note on dpkg triggers

The dpkg trigger facility allows events caused by the installation of one package (the triggering package) but of interest to another package be recorded and aggregated, and processed later (after all packages being installed in the current run have had a chance to trigger events, either explicitly or implicitly) by the interested packages. This feature simplifies various registration and system-update tasks and reduces duplication of processing.

At this point, there are only two kinds of triggers:

  1. Explicit Triggers: These can be activated by any program by running dpkg-trigger at any time, but ideally from a maintainer script.
  2. File Triggers: These are activated automatically by dpkg when a matching file is installed, upgraded or removed as part of a package. They may also be explicitly activated by running dpkg-trigger.

All triggers are named, and the names may consist only of printing 7-bit ascii characters.

Packages may express an interest in a trigger; this means that when any event activates the trigger, the package would like to perform some action7.

All trigger activations are associated with the package that caused the activation; an imply that the package installation will not be complete until all the interested and correctly installed packages have performed whatever action they intended to perform when they expressed an interest in the trigger. When a package is waiting for other packages to perform whatever actions they wanted to perform, the state it is in is called Triggers-Awaited state (Waiting-For-Triggered-Actions, really). The list of actions pending is whittled down as the interested packages perform the work, or fail (the interested packages leave the Triggers-Pending state state at that point, and no longer block the installation of this package).

When a trigger is activated, all the packages that expressed an interest in performing some work enter a Triggers-Pending state (should be called Need-to-do-some-work state). At some point, the package's postinst will be called with the list of events for which action is pending in order to let the package do the work.

Conclusion

In conclusion, here is the full activity diagram encompassing the above use cases. While this is certainly complex, it is still fairly easy to see how the use cases flow from the initial states (the green ellipses), through the decision points (the blue diamonds of the maintainer script execution), to the octagonal terminal states.

State diagram:Remove

Overall Activity Diagram

Footnotes:

1 The sequence diagrams are not created since the error unwind steps are hard to describe in a single sequence diagram, and the information is already present in the activity diagrams that are present.

2 Why not just use Marga's document as a basis? Well, this document has a different slant than Marga's documents: Firstly, it tries to be more complete, as befits something that is in policy, as opposed to the crystal clarity of the document Marga created (it tries to be as clear as it can, but does not elide conflicting packages, deconfiguration, and triggers). Secondly, this aims to become a normative, formal definition of the packaging system interface at a future time. Also, this document tries to take a more holistic view of state transitions of a package in the package management system, and does not have a maintainer script centric treatment (the script centric approach is certainly useful to the people developing the scripts). Finally, this document tries to hew closer to formal UML diagrams.

3 Conffiles are configuration files that are listed in the DEBIAN/conffiles control file

4 Usually this means that the installation of the package has activated some triggers, either explicitly in the postinst, or implicitly by installing some file, and at least one other package has expressed an interest in doing some work when the trigger has been activated.

5 Some configuration files might be unknown to dpkg because they are created and handled separately through the maintainer scripts. In this case, dpkg won't remove them by itself, but the package's postrm script (which is called by dpkg), has to take care of their removal during purge.

6 In other words, the old configuration files are not ever removed: either they are replaced with the new package's configuration files, or they are untouched

7 Think of actions like updating the manual page indices, which need only happen once at the end of the current installation run, instead of at the end of the installation of every package that has manual pages.

Author: Manoj Srivastava <srivasta@debian.org>

Date: <2009-10-16 Fri>

HTML generated by org-mode 6.31trans in emacs 23