# -*- mode: org; fill-column: 72 -*- #+STARTUP: hidestars showall #+TITLE: Maintainer scripts #+AUTHOR: Manoj Srivastava #+EMAIL: srivasta@debian.org #+DATE: <2009-10-16 Fri> #+LANGUAGE: en #+OPTIONS: H:4 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:nil tags:not-in-toc #+INFOJS_OPT: view:info toc:1 path:org-info.js tdepth:2 ftoc:t #+LINK_UP: http://www.golden-gryphon.com/blog/manoj/ #+LINK_HOME: http://www.golden-gryphon.com/ * Introduction :PROPERTIES: :CUSTOM_ID: Intro :END: #+ <> 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 diagrams[fn:1]. 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 [[http://women.debian.org/wiki/English/MaintainerScripts][excellent treatise]] on maintainer script invocations and return values[fn:2]. * Package States and Flags :PROPERTIES: :CUSTOM_ID: PackageStates :END: #+ <> /dpkg/ uses the following package states internally about all known packages + <<>>: The package is not installed on the system. + <<>>: Only the configuration files of the package exist on the system[fn:3]. + <<>>: The installation of the package has been started, but not completed for some reason. + <<>>: The package is unpacked, but not configured. + <<>>: The package is unpacked and configuration has been started, but not yet completed for some reason. + <<>>: The package awaits trigger processing by another package[fn:4]. + <<>>: Another package has activated a trigger that this package had earlier expressed an interest in, and now some work has to be done. + <<>>: The package has been unpacked and configured correctly, and is deemed installed. Apart from these states, /dpkg/ may also flag a package on a system. Currently, the only flag available is: + <<>>: 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 :PROPERTIES: :CUSTOM_ID: UseCases :END: #+ <> 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 :PROPERTIES: :CUSTOM_ID: Install :END: #+ <> 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. #+ATTR_HTML: title="State diagram for Simple installations" style="color:red;" alt="State diagram:Install" #+CAPTION: State transition diagram for package installation #+begin_ditaa InstallState.png -r -o -s 0.8 +--------------------------------------------------------+ | /-------------------+ | | | Not Installed | | | +------------------------>* cRED | | | | | | | | | +-------------------/ | | /---*-----------+ /------------------+ | | | Not Installed | | Half Installed | | | | cGRE *------------>* cRED | | | | | +------------------/ | | +---*------*----/ | | | | | | | +--------------------------+ | | | | | | v v | | /---*-----------+ /-------*----------+ | | | Installed | | Failed Config | | | | cGRE | | cRED | | | +---------------/ +------------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Collaboration diagram for Simple installations" style="color:red;" alt="State diagram:Install" #+CAPTION: Collaboration diagram for package installation #+begin_ditaa InstallCollab.png -r -o -s 0.8 +--------------------------------------------------------+ | /----------------+ /-----------------+ | | | | | | | | | DPKG | | preinst | | | | *---->* | | | | cBLK | | cGRE | | | +---*-----*----*-/ +-----------------/ | | | | | | | | | +---+ | | v v | | | /--*-----*------+ | /----------------+ | | | postinst | | | postrm | | | | | +-->* | | | | cGRE | | cRED | | | +---------------/ +----------------/ | | | | | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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 [[#Deconfigure][later]] in this document. #+ATTR_HTML: title="Activity diagram for Simple installations" style="color:red;" alt="State diagram:Install" #+CAPTION: Activity diagram for package installation #+begin_dot InstallActivity.png -Tpng digraph MaintainerScripts { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=true; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; /* Starting states */ "Not-Installed" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Maybe Stating or Terminal states */ "Installed\nV1" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* install V1*/ "V1->\npreinst install" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostrm abort-install" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostinst configure" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Deconfigure\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Remove\nMarked\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostinst triggered TP0..TPn" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; /* Failure terminal states */ "Half-Installed\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "If\nActive triggers\nTA0..TAn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "If\ntriggers pending\nTP0..TPn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Trigger Actions\nPending V1" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Waiting for\nTriggers V1" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Not-Installed" -> "Deconfigure\nPackages" [fontsize=11,fontname="Times-Bold",label="Install",color="cyan",fontcolor=lightskyblue]; "Deconfigure\nPackages" -> "V1->\npreinst install" [color="cyan",fontcolor]; "V1->\npreinst install" -> "V1->\npostrm abort-install" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npreinst install" -> "Remove\nMarked\nPackages" [fontsize=9,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages" -> "V1->\npostinst configure" [color="cyan"]; "V1->\npostrm abort-install" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm abort-install" -> "Not-Installed" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst configure" -> "Failed-Config\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst configure" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; subgraph cluster_trigger { "If\ntriggers pending\nTP0..TPn" -> "Trigger Actions\nPending V1" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "If\ntriggers pending\nTP0..TPn" -> "If\nActive triggers\nTA0..TAn" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "Trigger Actions\nPending V1" -> "V1->\npostinst triggered TP0..TPn" [color="cyan"]; "V1->\npostinst triggered TP0..TPn" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Waiting for\nTriggers V1" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Waiting for\nTriggers V1" -> "If\nActive triggers\nTA0..TAn" [color="cyan"]; } "V1->\npostinst triggered TP0..TPn" -> "Failed-Config\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Installed\nV1" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; labelloc="t"; label="Install Package\n"; fontsize="32"; } #+end_dot ** Remove and Purge an installed package :PROPERTIES: :CUSTOM_ID: Remove :END: #+ <> 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 [fn: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. #+ATTR_HTML: title="State diagram for package removal" style="color:red;" alt="State diagram:Remove" #+CAPTION: State transition diagram for package removal and purge #+begin_ditaa RemoveState.png -r -o -s 0.8 +--------------------------------------------------------+ | /------------------+ | | | | | | +------------------------>* Installed | | | | | cRED | | | | +------------------/ | | /---*-----------+ /------------------+ | | | Installed | | Half Installed | | | | cGRE |------------>* cRED | | | +---*------*----/ +------------------/ | | | | /------------------+ | | | | | | | | | +----------------->* Failed Config | | | v | cRED | | | /---*-----------+ +------------------/ | | | Config Files | /------------------+ | | | cGRE | | | | | | *------------>* Not Installed | | | +---* | | cGRE | | | | +---------------/ +------------------/ | | | | | | /---------------+ | | | | Config Files | | | +-->* cRED | | | | | | | +---------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Collaboration diagram for package removal" style="color:red;" alt="State diagram:Install" #+CAPTION: Collaboration diagram for package removal #+begin_ditaa RemoveCollab.png -r -o -s 0.8 +--------------------------------------------------------+ | /----------------+ /-----------------+ | | | | | | | | | DPKG *---->* prerm | | | | *--+ | cGRE | | | | cBLK | | +-----------------/ | | +---*-----*----*-/ | /-----------------+ | | | | | | | Conflictor | | | | | | +->* postinst cRED | | | | | | +-----------------/ | | v v +---+ | | /--*-----*------+ | /----------------+ | | | postrm | | | postinst | | | | | +-->* | | | | cGRE | | cRED | | | +---------------/ +----------------/ | | | | | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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). #+ATTR_HTML: title="Activity diagram for package removal" style="color:red;" alt="State diagram:Remove" #+CAPTION: Activity diagram for package removal and purge #+begin_dot RemoveActivity.png -Tpng digraph MaintainerScripts { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=true; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; /* Starting states */ "Installed\nV1" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Removal and Purge */ "V1->\nprerm remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostinst abort-remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostrm remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostrm purge" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "If Conflict" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Conflictor->\npostinst abort-remove\nin-favour\nfoo V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; /* Maybe Terminal states */ "Not-Installed" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; "Config-Files\nV1" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF00" ]; /* Failure terminal states */ "Half-Installed\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Installed\nV1" -> "V1->\nprerm remove" [fontsize=11,fontname="Times-Bold",label="Remove",color="cyan",fontcolor=lightskyblue]; "V1->\nprerm remove" -> "If Conflict" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If Conflict" -> "V1->\npostinst abort-remove" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "If Conflict" -> "Conflictor->\npostinst abort-remove\nin-favour\nfoo V1" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Conflictor->\npostinst abort-remove\nin-favour\nfoo V1" -> "Failed-Config\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "Conflictor->\npostinst abort-remove\nin-favour\nfoo V1" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="OK\n(del\nfiles)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\nprerm remove" -> "V1->\npostrm remove" [fontsize=9,fontname="Times-Roman",label="OK\n(del\nfiles)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-remove" -> "Failed-Config\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-remove" -> "Installed\nV1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostrm remove" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm remove" -> "Config-Files\nV1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Config-Files\nV1" -> "V1->\npostrm purge" [fontsize=11,fontname="Times-Bold",label="Purge\n(del\nconffile)",color="cyan",fontcolor=lightskyblue]; "V1->\npostrm purge" -> "Config-Files\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm purge" -> "Not-Installed" [fontsize=9,fontname="Times-Roman",label="OK\n(rm\nfilelist)",color="chartreuse",fontcolor="#009900",decorate="true"]; labelloc="t"; label="Remove Package\n"; fontsize="32"; } #+end_dot ** Reinstall a package :PROPERTIES: :CUSTOM_ID: Reinstall :END: #+ <> 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 [fn:6]. #+ATTR_HTML: title="State diagram for package reinstallation" style="color:red;" alt="State diagram:Reinstall" #+CAPTION: State transition diagram for package reinstallation #+begin_ditaa ReinstallState.png -r -o -s 0.8 +--------------------------------------------------------+ | /------------------+ | | | Config Files | | | +------------------------>* cRED | | | | +------------------/ | | /---*-----------+ /------------------+ | | | Config Files | | Half Installed | | | | cGRE *------------>* cRED | | | | | +------------------/ | | +---*------*----/ | | | | | | | +--------------------------+ | | | | | | v v | | /---*-----------+ /-------*----------+ | | | Installed | | Failed Config | | | | cGRE | | cRED | | | +---------------/ +------------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Collaboration diagram for package reinstallations" style="color:red;" alt="State diagram:Reinstall" #+CAPTION: Collaboration diagram for package reinstallation #+begin_ditaa ReinstallCollab.png -r -o -s 0.8 +--------------------------------------------------------+ | /----------------+ /-----------------+ | | | | | | | | | DPKG | | preinst | | | | *---->* | | | | cBLK | | cGRE | | | +---*-----*----*-/ +-----------------/ | | | | | | | | | +---+ | | v v | | | /--*-----*------+ | /----------------+ | | | postinst | | | postrm | | | | | +-->* | | | | cGRE | | cRED | | | +---------------/ +----------------/ | | | | | | cCCC | +--------------------------------------------------------+ #+end_ditaa As with the collaboration diagram, the activity diagram is indistinguishable from that of a straight install, *** Activity Diagram #+ATTR_HTML: title="Activity diagram for package reinstallation" style="color:red;" alt="State diagram:Reinstall" #+CAPTION: Activity diagram for package reinstallation #+begin_dot ReinstallActivity.png -Tpng digraph MaintainerScripts { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=true; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; /* Starting states */ "Config-Files\nV1" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF00" ]; /* reinstall V2 (config files from V1 present) */ "V2->\npreinst install V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostrm abort-install V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostinst configure V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Deconfigure\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Remove\nMarked\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostinst triggered TP0..TPn" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; /* Successful terminal states */ "Installed\nV2" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Failure terminal states */ "Half-Installed\nV2" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV2" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "If\nActive triggers\nTA0..TAn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "If\ntriggers pending\nTP0..TPn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Trigger Actions\nPending V2" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Waiting for\nTriggers V2" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Config-Files\nV1" -> "Deconfigure\nPackages" [fontsize=11,fontname="Times-Bold",label="Install V2",color="cyan",fontcolor=lightskyblue]; "Deconfigure\nPackages" -> "V2->\npreinst install V1" [color="cyan"]; "V2->\npreinst install V1" -> "V2->\npostrm abort-install V1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npreinst install V1" -> "Remove\nMarked\nPackages" [fontsize=9,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages" -> "V2->\npostinst configure V1" [color="cyan"]; "V2->\npostrm abort-install V1" -> "Half-Installed\nV2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-install V1" -> "Config-Files\nV1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostinst configure V1" -> "Failed-Config\nV2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostinst configure V1" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; subgraph cluster_trigger { "If\ntriggers pending\nTP0..TPn" -> "Trigger Actions\nPending V2" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "If\ntriggers pending\nTP0..TPn" -> "If\nActive triggers\nTA0..TAn" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "Trigger Actions\nPending V2" -> "V2->\npostinst triggered TP0..TPn" [color="cyan"]; "V2->\npostinst triggered TP0..TPn" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Waiting for\nTriggers V2" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Waiting for\nTriggers V2" -> "If\nActive triggers\nTA0..TAn" [color="cyan"]; } "V2->\npostinst triggered TP0..TPn" -> "Failed-Config\nV2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Installed\nV2" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; labelloc="t"; label="Reinstall Package\n"; fontsize="32"; } #+end_dot ** Upgrade a package to a new version :PROPERTIES: :CUSTOM_ID: Upgrade :END: #+ <> 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. #+ATTR_HTML: title="State diagram for package removal" style="color:red;" alt="State diagram:Upgrade" #+CAPTION: State transition diagram for package upgrades #+begin_ditaa UpgradeState.png -r -o -s 0.8 +--------------------------------------------------------+ | /-------------------+ | | | Installed V1 | | | +------------------------>* cRED | | | | +-------------------/ | | | /-------------------+ | | | | Unpacked V1 | | | | +------------------>* cRED | | | | | +-------------------/ | | /---*-----*-----+ /-------------------+ | | | Installed V1 | | Half Installed V1 | | | | cGRE *------------>* cRED | | | | | +-------------------/ | | +---*------*-*--/ /-------------------+ | | | | | | Failed Config V1 | | | | | +--------------->* cRED | | | | | +-------------------/ | | v +------------+ | | /---*-----------+ | /------------------+ | | | Installed V2 | | | Failed Config V2 | | | | cGRE | +---->* cRED | | | +---------------/ +------------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Collaboration diagram for package upgrades" style="color:red;" alt="State diagram:Upgrade" #+CAPTION: Collaboration diagram for package upgrade #+begin_ditaa UpgradeCollab.png -r -o -s 0.8 +----------------------------------------------------------------+ | /----------------+ /-----------------+ | | | OLD *<-------+ | NEW | | | | prerm | | +------->* prerm | | | | cGRE | | | | cRED | | | +----------------/ | | +-----------------/ | | | | /-----------------+ | | /----------------+ | | | | | | | NEW *<--+ | | +--->* NEW | | | | preinst | | | | | | prerm | | | | cGRE | /-*----*-*---*--+ | cRED | | | +----------------/ | DPKG | +-----------------/ | | /----------------+ | cBLK | /-----------------+ | | | OLD | +-*--*--*--*--*-/ | OLD | | | | postrm | | | | | | | postinst | | | | cGRE *---+ | | | +-->* cRED | | | | | | | | +-----------------/ | | +----------------/ | | | | | /----------------+ | | | /-----------------+ | | | NEW | | | | | OLD | | | | postinst *<-----+ | +----->* postrm | | | | cGRE | | | cRED | | | +----------------/ v +-----------------+ | | /-------*--------+ | | | OLD | | | | preinst | | | | cRED | | | +----------------/ | | cCCC | +----------------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Activity diagram for package removal" style="color:red;" alt="State diagram:Upgrade" #+CAPTION: Activity diagram for package upgrades #+begin_dot UpgradeActivity.png -Tpng digraph MaintainerScripts { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=true; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; /* Starting states */ "Installed\nV1" [shape=ellipse,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Upgrade from V1 to V2 */ "V1->\nprerm upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\nprerm failed-upgrade V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostinst abort-upgrade V2\na" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npreinst upgrade V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostrm abort-upgrade V1\na" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostinst abort-upgrade V2\nb" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npostrm upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostrm failed-upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V1->\npreinst abort-upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostrm abort-upgrade V1\nb" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostinst configure V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "V2->\npostinst triggered TP0..TPn" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Installed\nV2" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Failure terminal states */ "Half-Installed\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV2" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Unpacked\nV1" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Deconfigure\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Remove\nMarked\nPackages" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "If\nActive triggers\nTA0..TAn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "If\ntriggers pending\nTP0..TPn" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Trigger Actions\nPending V2" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Waiting for\nTriggers V2" [shape=house,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Installed\nV1" -> "V1->\nprerm upgrade V2" [fontsize=11,fontname="Times-Bold",label="Install V2",color="cyan",fontcolor=lightskyblue]; "V1->\nprerm upgrade V2" -> "V2->\nprerm failed-upgrade V1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\nprerm upgrade V2" -> "Deconfigure\nPackages" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\nprerm failed-upgrade V1" -> "V1->\npostinst abort-upgrade V2\na" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\nprerm failed-upgrade V1" -> "Deconfigure\nPackages" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-upgrade V2\na" -> "Failed-Config\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-upgrade V2\na" -> "Installed\nV1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Deconfigure\nPackages" -> "V2->\npreinst upgrade V1" [color="cyan"]; "V2->\npreinst upgrade V1" -> "V2->\npostrm abort-upgrade V1\na" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npreinst upgrade V1" -> "Remove\nMarked\nPackages" [fontsize=9,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages" -> "V1->\npostrm upgrade V2" [color="cyan"]; "V2->\npostrm abort-upgrade V1\na" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-upgrade V1\na" -> "V1->\npostinst abort-upgrade V2\nb" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-upgrade V2\nb" -> "Unpacked\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-upgrade V2\nb" -> "Installed\nV1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostrm upgrade V2" -> "V2->\npostrm failed-upgrade V2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm upgrade V2" -> "V2->\npostinst configure V1" [fontsize=9,fontname="Times-Roman",label="OK\n(del\nold)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostrm failed-upgrade V2" -> "V1->\npreinst abort-upgrade V2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm failed-upgrade V2" -> "V2->\npostinst configure V1" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npreinst abort-upgrade V2" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npreinst abort-upgrade V2" -> "V2->\npostrm abort-upgrade V1\nb" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostrm abort-upgrade V1\nb" -> "Half-Installed\nV1" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-upgrade V1\nb" -> "V1->\npostinst abort-upgrade V2\nb" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostinst configure V1" -> "Failed-Config\nV2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostinst configure V1" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; subgraph cluster_trigger { "If\ntriggers pending\nTP0..TPn" -> "Trigger Actions\nPending V2" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "If\ntriggers pending\nTP0..TPn" -> "If\nActive triggers\nTA0..TAn" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "Trigger Actions\nPending V2" -> "V2->\npostinst triggered TP0..TPn" [color="cyan"]; "V2->\npostinst triggered TP0..TPn" -> "If\ntriggers pending\nTP0..TPn" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Waiting for\nTriggers V2" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Waiting for\nTriggers V2" -> "If\nActive triggers\nTA0..TAn" [color="cyan"]; } "V2->\npostinst triggered TP0..TPn" -> "Failed-Config\nV2" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If\nActive triggers\nTA0..TAn" -> "Installed\nV2" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; labelloc="t"; label="Upgrade Package\n"; fontsize="32"; } #+end_dot * Handling Conflicts :PROPERTIES: :CUSTOM_ID: HandlingConflicts :END: #+ <> 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 :PROPERTIES: :CUSTOM_ID: Deconfigure :END: #+ <> 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. #+ATTR_HTML: title="State diagram for package deconfiguration" style="color:red;" alt="State diagram:Deconfigure" #+CAPTION: State diagram for package deconfiguration #+begin_ditaa DeconfigureState.png -r -o -s 0.8 +--------------------------------------------------------+ | /------------------+ | | /------------+ | Half Installed | | | | Installed *--------->* cRED | | | | cGRE | +------------------/ | | +--*-----*---/ | | | | /------------------+ | | | | | Half Installed | | | | +------------->* Dependents | | | | | cRED | | | v +------------------/ | | /--*-------------+ | | | Deconfigured | | | | cGRE | | | +----------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Collaboration diagram for deconfiguring packages " style="color:red;" alt="State diagram:Deconfigure" #+CAPTION: Collaboration diagram for package deconfiguration #+begin_ditaa DeconfigureCollab.png -r -o -s 0.8 +--------------------------------------------------------+ | /----------------+ /-----------------+ | | | Conflictor's | | Conflictor's | | | | prerm *<----+ +--->* postinst | | | | cGRE | | | | | | | | | | | | cRED | | | +----------------/ | | +-----------------/ | | v v | | /----*----*----+ | | | DPKG | | | | cBLK | | | +----*----*----/ | | | | | | /---------------+ | | /----------------+ | | | Conflictor's | | | | Conflictors's | | | | Dependent's | | | | Dependent's | | | | prerm *<----+ +--->* postinst | | | | cGRE | | cRED | | | +---------------/ +----------------/ | | cCCC | +--------------------------------------------------------+ #+end_ditaa *** 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. #+ATTR_HTML: title="Activity diagram for package deconfiguration" style="color:red;" alt="State diagram:Deconfigure" #+CAPTION: Activity diagram for package deconfiguration #+begin_dot DeconfiureActivity.png -Tpng digraph ConflictHandling { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=false; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; "a" [shape=point,style=filled,fillcolor="gold",fontsize=1,fontname="Times-Roman"]; "if conflicted package K0..Kn\ni=0,g=0" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Ki->\nprerm deconfigure foo V2\nRg++=Ki" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Ki->\npostinst abort-deconfigure\nin-favour foo Vfoo" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Half Installed\nKi" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "if dependents Ki Di0..Dim\nj=0" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "Dij->\nprerm deconfigure\nin-favour foo V2\nremoving Ki Vi\nRg++=Dij" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Dij->\npostinst abort-deconfigure\nin-favour foo V2\nremoving Ki Vi" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=9,fontname="Times-Roman"]; "Half Installed\nDij" [shape=octagon,fontsize=11,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "if j++==m" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "if i++==n" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=9,fontname="Times-Roman"]; "z" [shape=point,style=filled,fillcolor="gold",fontsize=1,fontname="Times-Roman"]; "a" -> "if conflicted package K0..Kn\ni=0,g=0" [color="lightsalmon"]; "if conflicted package K0..Kn\ni=0,g=0" -> "z" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; subgraph cluster_deconfigure { "if conflicted package K0..Kn\ni=0,g=0" -> "Ki->\nprerm deconfigure foo V2\nRg++=Ki" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Ki->\nprerm deconfigure foo V2\nRg++=Ki" -> "Ki->\npostinst abort-deconfigure\nin-favour foo Vfoo" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "Ki->\nprerm deconfigure foo V2\nRg++=Ki" -> "if dependents Ki Di0..Dim\nj=0" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Ki->\npostinst abort-deconfigure\nin-favour foo Vfoo" -> "Half Installed\nKi" [color="firebrick"]; "if dependents Ki Di0..Dim\nj=0" -> "if i++==n" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "if dependents Ki Di0..Dim\nj=0" -> "Dij->\nprerm deconfigure\nin-favour foo V2\nremoving Ki Vi\nRg++=Dij" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Dij->\nprerm deconfigure\nin-favour foo V2\nremoving Ki Vi\nRg++=Dij" -> "Dij->\npostinst abort-deconfigure\nin-favour foo V2\nremoving Ki Vi" [fontsize=9,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "Dij->\nprerm deconfigure\nin-favour foo V2\nremoving Ki Vi\nRg++=Dij" -> "if j++==m" [fontsize=9,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Dij->\npostinst abort-deconfigure\nin-favour foo V2\nremoving Ki Vi" -> "Half Installed\nDij" [color="firebrick"]; "if j++==m" -> "Dij->\nprerm deconfigure\nin-favour foo V2\nremoving Ki Vi\nRg++=Dij" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "if j++==m" -> "if i++==n" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "if i++==n" -> "Ki->\nprerm deconfigure foo V2\nRg++=Ki" [fontsize=9,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; } "if i++==n" -> "z" [fontsize=9,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; labelloc="t"; label="Deconfigure\n"; fontsize="32"; } #+end_dot ** Remove Conflicting Packages :PROPERTIES: :CUSTOM_ID: RemoveConflicts :END: #+ <> 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 [[#Remove][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 action[fn:7]. 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 :PROPERTIES: :ID: b1107bfa-8e5c-49d1-998f-4f0a0caabf18 :CUSTOM_ID: Conclusion :END: #+ <> 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. #+ATTR_HTML: title="Overall Activity Diagram" style="color:red;" alt="State diagram:Remove" #+CAPTION: Overall Activity Diagram #+begin_dot Activity.png -Tpng digraph MaintainerScripts { bgcolor=gray3; center="true"; charset="iso-8859-1"; compound=true; concentrate=true; pack=true; packmode="node"; outputorder="nodesfirst"; graph [color="aliceblue",fontcolor="azure"]; /* Starting states */ "Not-Installed" [shape=ellipse,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Maybe Stating or Terminal states */ "Config-Files\nV1" [shape=ellipse,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#99FF00" ]; "Installed\nV1" [shape=ellipse,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* install V1*/ "V1->\npreinst install" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostrm abort-install" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostinst configure" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Deconfigure\nPackages\na" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Deconfigure\nPackages\nb" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Deconfigure\nPackages\nc" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Remove\nMarked\nPackages\na" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Remove\nMarked\nPackages\nb" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "Remove\nMarked\nPackages\nc" [shape=box,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostinst triggered TP0..TPn" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; /* Removal and Purge */ "V1->\nprerm remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostinst abort-remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostrm remove" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostrm purge" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "If Conflict" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=8,fontname="Times-Roman"]; "Conflictor->\npostinst\nabort-remove\nin-favour\nfoo V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; /* reinstall V2 (config files from V1 present) */ "V2->\npreinst install V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostrm abort-install V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostinst configure V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostinst triggered TP0..TPn" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; /* Upgrade from V1 to V2 */ "V1->\nprerm upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\nprerm failed-upgrade V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostinst abort-upgrade V2\na" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npreinst upgrade V1" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostrm abort-upgrade V1\na" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostinst abort-upgrade V2\nb" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npostrm upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostrm failed-upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V1->\npreinst abort-upgrade V2" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; "V2->\npostrm abort-upgrade V1\nb" [shape=diamond,style=filled,fillcolor="#CCFFFF",fontsize=8,fontname="Times-Roman"]; /* Successful terminal states */ "Installed\nV2" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#99FF99" ]; /* Failure terminal states */ "Half-Installed\nV1" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Half-Installed\nV2" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV1" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Failed-Config\nV2" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "Unpacked\nV1" [shape=octagon,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="#FFCCFF" ]; "If\nActive triggers\nTA0..TAn\na" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=8,fontname="Times-Roman"]; "If\ntriggers pending\nTP0..TPn\na" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=8,fontname="Times-Roman"]; "Trigger Actions\nPending V1\na" [shape=house,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Waiting for\nTriggers V1\na" [shape=house,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "If\nActive triggers\nTA0..TAn\nb" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=8,fontname="Times-Roman"]; "If\ntriggers pending\nTP0..TPn\nb" [shape=diamond,style=filled,fillcolor="antiquewhite",fontsize=8,fontname="Times-Roman"]; "Trigger Actions\nPending V2\nb" [shape=house,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; "Waiting for\nTriggers V2\nb" [shape=house,fontsize=10,fontname="Times-Bold",style=filled,fillcolor="bisque2" ]; /* Install */ "Not-Installed" -> "Deconfigure\nPackages\na" [fontsize=10,fontname="Times-Bold",label="Install",color="cyan",fontcolor=lightskyblue]; "Deconfigure\nPackages\na" -> "V1->\npreinst install" [color="cyan"]; "V1->\npreinst install" -> "V1->\npostrm abort-install" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npreinst install" -> "Remove\nMarked\nPackages\na" [fontsize=8,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages\na" -> "V1->\npostinst configure" [color="cyan"]; "V1->\npostrm abort-install" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm abort-install" -> "Not-Installed" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst configure" -> "Failed-Config\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst configure" -> "If\ntriggers pending\nTP0..TPn\na" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; subgraph trigger_a { "If\ntriggers pending\nTP0..TPn\na" -> "Trigger Actions\nPending V1\na" [fontsize=8,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "If\ntriggers pending\nTP0..TPn\na" -> "If\nActive triggers\nTA0..TAn\na" [fontsize=8,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "Trigger Actions\nPending V1\na" -> "V1->\npostinst triggered TP0..TPn" [color="cyan"]; "V1->\npostinst triggered TP0..TPn" -> "If\ntriggers pending\nTP0..TPn\na" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "If\nActive triggers\nTA0..TAn\na" -> "Waiting for\nTriggers V1\na" [fontsize=8,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Waiting for\nTriggers V1\na" -> "If\nActive triggers\nTA0..TAn\na" [color="cyan"]; } "V1->\npostinst triggered TP0..TPn" -> "Failed-Config\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If\nActive triggers\nTA0..TAn\na" -> "Installed\nV1" [fontsize=8,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; /* Removal */ "Installed\nV1" -> "V1->\nprerm remove" [fontsize=10,fontname="Times-Bold",label="Remove",color="cyan",fontcolor=lightskyblue]; "V1->\nprerm remove" -> "If Conflict" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If Conflict" -> "V1->\npostinst abort-remove" [fontsize=8,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "If Conflict" -> "Conflictor->\npostinst\nabort-remove\nin-favour\nfoo V1" [fontsize=8,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Conflictor->\npostinst\nabort-remove\nin-favour\nfoo V1" -> "Failed-Config\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "Conflictor->\npostinst\nabort-remove\nin-favour\nfoo V1" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="OK\n(del\nfiles)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\nprerm remove" -> "V1->\npostrm remove" [fontsize=8,fontname="Times-Roman",label="OK\n(del\nfiles)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-remove" -> "Failed-Config\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-remove" -> "Installed\nV1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostrm remove" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm remove" -> "Config-Files\nV1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Config-Files\nV1" -> "V1->\npostrm purge" [fontsize=10,fontname="Times-Bold",label="Purge\n(del\nconffile)",color="cyan",fontcolor=lightskyblue]; "V1->\npostrm purge" -> "Config-Files\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm purge" -> "Not-Installed" [fontsize=8,fontname="Times-Roman",label="OK\n(rm\nfilelist)",color="chartreuse",fontcolor="#009900",decorate="false"]; /* reinstall */ "Config-Files\nV1" -> "Deconfigure\nPackages\nb" [fontsize=10,fontname="Times-Bold",label="Install V2",color="cyan",fontcolor=lightskyblue]; "Deconfigure\nPackages\nb" -> "V2->\npreinst install V1" [color="cyan"]; "V2->\npreinst install V1" -> "V2->\npostrm abort-install V1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npreinst install V1" -> "Remove\nMarked\nPackages\nb" [fontsize=8,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages\nb" -> "V2->\npostinst configure V1" [color="cyan"]; "V2->\npostrm abort-install V1" -> "Half-Installed\nV2" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-install V1" -> "Config-Files\nV1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostinst configure V1" -> "Failed-Config\nV2" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostinst configure V1" -> "If\ntriggers pending\nTP0..TPn\nb" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; subgraph trigger_b { "If\ntriggers pending\nTP0..TPn\nb" -> "Trigger Actions\nPending V2\nb" [fontsize=8,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "If\ntriggers pending\nTP0..TPn\nb" -> "If\nActive triggers\nTA0..TAn\nb" [fontsize=8,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; "Trigger Actions\nPending V2\nb" -> "V2->\npostinst triggered TP0..TPn" [color="cyan"]; "V2->\npostinst triggered TP0..TPn" -> "If\ntriggers pending\nTP0..TPn\nb" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "If\nActive triggers\nTA0..TAn\nb" -> "Waiting for\nTriggers V2\nb" [fontsize=8,fontname="Times-Roman",label="Yes",color="plum",fontcolor="plum",decorate="true"]; "Waiting for\nTriggers V2\nb" -> "If\nActive triggers\nTA0..TAn\nb" [color="cyan"]; } "V2->\npostinst triggered TP0..TPn" -> "Failed-Config\nV2" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "If\nActive triggers\nTA0..TAn\nb" -> "Installed\nV2" [fontsize=8,fontname="Times-Roman",label="No",color="khaki",fontcolor="khaki",decorate="true"]; /* Upgrading */ "Installed\nV1" -> "V1->\nprerm upgrade V2" [fontsize=10,fontname="Times-Bold",label="Install V2",color="cyan",fontcolor=lightskyblue]; "V1->\nprerm upgrade V2" -> "V2->\nprerm failed-upgrade V1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\nprerm upgrade V2" -> "Deconfigure\nPackages\nc" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\nprerm failed-upgrade V1" -> "V1->\npostinst abort-upgrade V2\na" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\nprerm failed-upgrade V1" -> "Deconfigure\nPackages\nc" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-upgrade V2\na" -> "Failed-Config\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-upgrade V2\na" -> "Installed\nV1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "Deconfigure\nPackages\nc" -> "V2->\npreinst upgrade V1" [color="cyan"]; "V2->\npreinst upgrade V1" -> "V2->\npostrm abort-upgrade V1\na" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npreinst upgrade V1" -> "Remove\nMarked\nPackages\nc" [fontsize=8,fontname="Times-Roman",label="OK\n(unpack)",color="chartreuse",fontcolor="#009900",decorate="true"]; "Remove\nMarked\nPackages\nc" -> "V1->\npostrm upgrade V2" [color="cyan"]; "V2->\npostrm abort-upgrade V1\na" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-upgrade V1\na" -> "V1->\npostinst abort-upgrade V2\nb" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostinst abort-upgrade V2\nb" -> "Unpacked\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostinst abort-upgrade V2\nb" -> "Installed\nV1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npostrm upgrade V2" -> "V2->\npostrm failed-upgrade V2" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npostrm upgrade V2" -> "V2->\npostinst configure V1" [fontsize=8,fontname="Times-Roman",label="OK\n(del\nold)",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostrm failed-upgrade V2" -> "V1->\npreinst abort-upgrade V2" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm failed-upgrade V2" -> "V2->\npostinst configure V1" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V1->\npreinst abort-upgrade V2" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V1->\npreinst abort-upgrade V2" -> "V2->\npostrm abort-upgrade V1\nb" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; "V2->\npostrm abort-upgrade V1\nb" -> "Half-Installed\nV1" [fontsize=8,fontname="Times-Roman",label="Failed",color="firebrick",fontcolor="#FF6633",decorate="true"]; "V2->\npostrm abort-upgrade V1\nb" -> "V1->\npostinst abort-upgrade V2\nb" [fontsize=8,fontname="Times-Roman",label="OK",color="chartreuse",fontcolor="#009900",decorate="true"]; labelloc="t"; label="Scripts Overview\n"; fontsize="32"; } #+end_dot * Footnotes [fn: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. [fn: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. [fn:3] Conffiles are configuration files that are listed in the =DEBIAN/conffiles= control file [fn: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. [fn: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. [fn: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 [fn: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. * COMMENT Emacs local variables # Local variables: # mode: org # fill-column: 72 # End: