|
|
@@ -0,0 +1,895 @@
|
|
|
+TRIGGERS
|
|
|
+========
|
|
|
+
|
|
|
+Introduction
|
|
|
+------------
|
|
|
+
|
|
|
+A dpkg trigger is a facility that allows events caused by one package
|
|
|
+but of interest to another package to be recorded and aggregated, and
|
|
|
+processed later by the interested package. This feature simplifies
|
|
|
+various registration and system-update tasks and reduces duplication
|
|
|
+of processing.
|
|
|
+
|
|
|
+(NB: Triggers are intended for events that occur during package
|
|
|
+installation, not events that occur in general operation.)
|
|
|
+
|
|
|
+
|
|
|
+Concepts
|
|
|
+--------
|
|
|
+
|
|
|
+Each trigger is named, and at any time zero or more packages may be
|
|
|
+interested in it.
|
|
|
+
|
|
|
+We currently envisage three kinds of triggers:
|
|
|
+ * Explicit triggers. These can be activated by any program
|
|
|
+ by running dpkg-trigger (at any time, but ideally from a maintainer
|
|
|
+ script).
|
|
|
+ * 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.
|
|
|
+ * Future kinds of special triggers, which are activated by magic code
|
|
|
+ in dpkg itself. Currently none are defined besides file triggers.
|
|
|
+
|
|
|
+A trigger is always activated by a particular package.
|
|
|
+
|
|
|
+Trigger names contain only printing 7-bit ascii characters (no
|
|
|
+whitespace). Each trigger kind has a distinct subset of the trigger
|
|
|
+name space so that the kind can be determined from the name. After we
|
|
|
+run out of straightforward syntaxes, we will use <kind>:<details>.
|
|
|
+
|
|
|
+When a trigger is activated, it becomes pending for every package
|
|
|
+which is interested in the trigger at that time. Each package has a
|
|
|
+list of zero or more pending triggers. Repeated activation of the
|
|
|
+same trigger has no additional effect. Note that in general a trigger
|
|
|
+will not be processed immediately when it is activated; processing is
|
|
|
+deferred until it is convenient (as described below).
|
|
|
+
|
|
|
+At a trigger activation, the interested packages(s) are added to the
|
|
|
+triggering package's list of triggers-awaited packages; the triggering
|
|
|
+package is said to await the trigger processing.
|
|
|
+
|
|
|
+A package which has pending triggers, or which awaits triggers, is not
|
|
|
+considered properly installed. There are two new dpkg status values,
|
|
|
+`triggers-pending' and `triggers-awaited', which lie between
|
|
|
+`config-failed' and `installed'.
|
|
|
+
|
|
|
+
|
|
|
+Details - Overview table
|
|
|
+------------------------
|
|
|
+
|
|
|
+ Status Pending Awaited Satisfies Remedy
|
|
|
+ triggers triggers Depends
|
|
|
+
|
|
|
+ unpacked never maybe No postinst configure
|
|
|
+ c.-failed never maybe No postinst configure (when requested)
|
|
|
+ t.-awaited yes always No postinst triggered + fix awaited pkg(s)
|
|
|
+ t.-awaited no always No fix awaited package(s)
|
|
|
+ t.-pending always never Yes postinst triggered
|
|
|
+ installed never never Yes n/a
|
|
|
+
|
|
|
+Packages in t-awaited and t-pending demand satisfaction of their
|
|
|
+dependencies just like packages in installed.
|
|
|
+
|
|
|
+
|
|
|
+Details - triggering package
|
|
|
+----------------------------
|
|
|
+
|
|
|
+When a package T activates a trigger in which a package I is
|
|
|
+interested, I is added to the list of packages whose trigger
|
|
|
+processing is awaited by T. Zero or more packages I may be added as a
|
|
|
+result of any particular trigger activation, depending on how many
|
|
|
+packages were interested. (If T chooses, explicit trigger activation
|
|
|
+using dpkg-trigger of I by T need not make T become triggers-awaited
|
|
|
+in this way..)
|
|
|
+
|
|
|
+A package which awaits trigger processing but would otherwise be
|
|
|
+`installed' or `triggers-pending' is considered to be in state
|
|
|
+`triggers-awaited'. Packages in `triggers-awaited' do not satisfy
|
|
|
+Depends dependencies.
|
|
|
+
|
|
|
+Every triggered package I in T's list of awaited packages either has a
|
|
|
+nonempty list of pending triggers, or is in `config-failed' or worse.
|
|
|
+When I enters `installed' (or `config-files' or `not-installed'), the
|
|
|
+entry in T's list of awaited packages is removed so that T may, if it
|
|
|
+no longer awaits any packages, become `installed' or
|
|
|
+`triggers-pending'.
|
|
|
+
|
|
|
+Packages in `config-files' or `not-installed' do not await triggers.
|
|
|
+
|
|
|
+
|
|
|
+Details - triggered package
|
|
|
+---------------------------
|
|
|
+
|
|
|
+When one of the triggers in which a package is interested is
|
|
|
+activated, the triggered package has the trigger added to its list of
|
|
|
+pending triggers. Packages with a nonempty list of pending triggers
|
|
|
+which would otherwise be in state `installed' are in state
|
|
|
+`triggers-pending' instead, so if the package was previously
|
|
|
+`installed' it becomes `triggers-pending'.
|
|
|
+
|
|
|
+If a package has nonempty lists both of pending and awaited triggers,
|
|
|
+then it is in `triggers-awaited'. Nevertheless efforts will still be
|
|
|
+made to process its triggers so as to make the list of pending
|
|
|
+triggers empty.
|
|
|
+
|
|
|
+To restore a package in state `triggers-pending' to `installed', or to
|
|
|
+process pending triggers of a package with both pending and awaited
|
|
|
+triggers, dpkg will run the postinst script:
|
|
|
+ postinst triggered "<trigger-name> <trigger-name> ..."
|
|
|
+
|
|
|
+This will be attempted for each relevant package at the end of each
|
|
|
+dpkg run; so, normally, in the same dpkg run as the event which made
|
|
|
+the package go to `triggers-pending'. This leaves packages in
|
|
|
+reasonable states by default.
|
|
|
+
|
|
|
+If the `postinst triggered' run fails the package goes to
|
|
|
+`config-failed', so that the trigger processing will not be attempted
|
|
|
+again until explictly requested.
|
|
|
+
|
|
|
+
|
|
|
+ |
|
|
|
+ V
|
|
|
+ ,------------.
|
|
|
+ | unpacked |
|
|
|
+ `------------'
|
|
|
+ |
|
|
|
+ |
|
|
|
+ (automatic)| ,----------.
|
|
|
+ | | config- |
|
|
|
+ | | failed |
|
|
|
+ | `----------'
|
|
|
+ | | ^
|
|
|
+ | | |
|
|
|
+ |,---<--' | ,------------------------------.
|
|
|
+ | (user | | triggers-pending |
|
|
|
+ postinst | request) | | or |
|
|
|
+ "configure" | | | t.-awaited with some pending |
|
|
|
+ | | `------------------------------'
|
|
|
+ | | | ^
|
|
|
+ |`----->------'| | |
|
|
|
+ | error | postinst | |
|
|
|
+ | | "triggered" | | trigger(s)
|
|
|
+ | | (automatic) | | activated
|
|
|
+ | | | |
|
|
|
+ | `-----<-----------'| |
|
|
|
+ | error | |
|
|
|
+ | | |
|
|
|
+ V V |
|
|
|
+ ,--------------------------------------------------.
|
|
|
+ | installed or t.-awaited with none pending |
|
|
|
+ `--------------------------------------------------'
|
|
|
+
|
|
|
+Packages in `config-failed' or worse are never considered to have
|
|
|
+lists of pending triggers. A package whose postinst is being run
|
|
|
+can however acquire pending triggers during that run (ie, a package
|
|
|
+can trigger itself).
|
|
|
+
|
|
|
+This means that if a triggering package T awaits trigger processing by
|
|
|
+an interested package I, and I goes to `config-failed' or worse (eg,
|
|
|
+during unpack for upgrade), then when I is reconfigured (goes to
|
|
|
+`installed') or removed, T will no longer await processing by I, so
|
|
|
+that T may automatically go from `triggers-awaited' to `installed'.
|
|
|
+
|
|
|
+Or to put it another way, triggered actions are considered irrelevant
|
|
|
+if the interested package I is not configured. When I's postinst is
|
|
|
+called with `configure', it must do whatever actions are necessary to
|
|
|
+deal with any trigger activations which might have occured while it
|
|
|
+was not configured, just as if the package was being configured for
|
|
|
+the first time.
|
|
|
+
|
|
|
+Trigger processing should be idempotent. The list of triggers being
|
|
|
+processed is provided to the postinst only so that it can optimise
|
|
|
+away redundant processing.
|
|
|
+
|
|
|
+In that case, where an interested package has more than one trigger
|
|
|
+and wants to process them differently, the list of triggers can be can
|
|
|
+be examined in a shell script like this:
|
|
|
+ case " $3 " in
|
|
|
+ *" trigger-name-a "*) process-trigger-a ;;
|
|
|
+ esac
|
|
|
+Generally each trigger name should be tested for separately, as the
|
|
|
+postinst will often be called for several triggers at once.
|
|
|
+
|
|
|
+Note that if a package both activates triggers in other packages, and
|
|
|
+is interested in triggers of its own, its postinst may run for trigger
|
|
|
+processing before the postinst(s) of the package(s) it has triggered.
|
|
|
+
|
|
|
+
|
|
|
+Timing guarantees, races, etc.
|
|
|
+------------------------------
|
|
|
+
|
|
|
+Activating a trigger will not have any immediate effect, although
|
|
|
+putative resulting status changes will show up in dpkg --status etc.
|
|
|
+(Putative because the actual status changes may depend on the state of
|
|
|
+trigger interests when dpkg processes the trigger activation into
|
|
|
+the status database, rather than that when dpkg --status is run.)
|
|
|
+
|
|
|
+A package is only guaranteed to become notified of a trigger
|
|
|
+activation if it is continuously interested in the trigger, and never
|
|
|
+in `config-failed' or worse, during the period from when the trigger
|
|
|
+is activated until dpkg runs the package postinst (either due to
|
|
|
+--configure --pending, or at the end of the relevant run, as described
|
|
|
+above). Subsequent to activation and before notification, the
|
|
|
+interested package will not be considered in state `installed', so
|
|
|
+long as the package remains interested, and the triggering package
|
|
|
+will not be considered `installed'.
|
|
|
+
|
|
|
+If the package is not in state `installed', `triggers-pending' or
|
|
|
+`triggers-awaited' then pending triggers are not accumulated.
|
|
|
+However, if such a package (between `half-installed' and
|
|
|
+`config-failed' inclusive) declares some trigger interests then the
|
|
|
+triggering packages *will* await their configuration (which implies
|
|
|
+completion of any necessary trigger processing) or removal.
|
|
|
+
|
|
|
+It is not defined in what order triggers will run. dpkg will make
|
|
|
+some effort to minimise redundant work in the case where many packages
|
|
|
+have postinst trigger processing activating another package's triggers
|
|
|
+(for example, by processing triggers in fifo order during a single
|
|
|
+dpkg run). Cycles in the triggering graph are prohibited and will
|
|
|
+eventually, perhaps after some looping, be detected by dpkg and cause
|
|
|
+trigger processing to fail; when this happens one of the packages
|
|
|
+involved will be put in state `config-failed' so that the trigger loop
|
|
|
+will not be reattempted. See `Cycle detection' below.
|
|
|
+
|
|
|
+
|
|
|
+Explicit triggers
|
|
|
+-----------------
|
|
|
+
|
|
|
+Explicit triggers have names with the same syntax as package names,
|
|
|
+*but* should *not* normally be named identically to a package.
|
|
|
+
|
|
|
+When choosing an explicit trigger name it is usually good to include a
|
|
|
+relevant package name or some other useful identifier to help make the
|
|
|
+trigger name unique. On the other hand, explicit triggers should
|
|
|
+generally not be renamed just because the interested or triggering
|
|
|
+packages' names change.
|
|
|
+
|
|
|
+Explicit trigger names form part of the interface between packages.
|
|
|
+Therefore in case of wider use of any trigger the name and purpose
|
|
|
+should be discussed in the usual way and documented in the appropriate
|
|
|
+packaging guidelines (eg, in policy).
|
|
|
+
|
|
|
+
|
|
|
+File triggers
|
|
|
+-------------
|
|
|
+
|
|
|
+File triggers have names of the form
|
|
|
+ /path/to/directory/or/file
|
|
|
+and are activated when the specified filesystem object, or any object
|
|
|
+under the specified subdirectory, is created, updated or deleted by
|
|
|
+dpkg during package unpack or removal. The pathname must be absolute.
|
|
|
+
|
|
|
+File triggers should not generally be used without mutual consent.
|
|
|
+The use of a file trigger, and the name of the trigger used, should be
|
|
|
+stated in policy, so that a package which creates a relevant file in a
|
|
|
+maintainer script can activate the trigger explictly.
|
|
|
+
|
|
|
+File triggers must definitely not be used as an escalation tool in
|
|
|
+disagreements between different packages as to the desired contents of
|
|
|
+the filesystem. Trigger activation due to a particular file should
|
|
|
+not generally modify that file again.
|
|
|
+
|
|
|
+Configuration files (whether dpkg-handled conffiles or not), or any
|
|
|
+other files which are modified at times other than package management,
|
|
|
+should not rely on file triggers detecting all modifications; dpkg
|
|
|
+triggers are not a general mechanism for filesystem monitoring.
|
|
|
+
|
|
|
+If there are or might be directory symlinks which result in packages
|
|
|
+referring to files by different names, then to be sure of activation
|
|
|
+all of the paths which might be included in packages should be listed.
|
|
|
+The path specified by the interested package is matched against the
|
|
|
+path included in the triggering package, not against the truename of
|
|
|
+the file as installed. Only textually identical filenames (or
|
|
|
+filenames where the interest is a directory prefix of the installed
|
|
|
+file) are guaranteed to match.
|
|
|
+
|
|
|
+A file trigger is guaranteed to be activated before the file in
|
|
|
+question is modified by dpkg; on the other hand, a file trigger might
|
|
|
+be activated even though no file was actually modified. Changes made
|
|
|
+by dpkg to the link count of a file, or to solely the inode number
|
|
|
+(ie, if dpkg atomically replaces it with another identical file), are
|
|
|
+not guaranteed to cause trigger activation.
|
|
|
+
|
|
|
+Because of the restriction on trigger names, it is not possible to
|
|
|
+declare a file trigger for a directory whose name contains whitespace,
|
|
|
+i18n characters, etc. Such a trigger should not be necessary.
|
|
|
+
|
|
|
+
|
|
|
+Package declarations regarding triggers
|
|
|
+---------------------------------------
|
|
|
+
|
|
|
+A package declares its relationship to some trigger(s) by including a
|
|
|
+`triggers' file in its control archive (ie, DEBIAN/triggers during
|
|
|
+package creation). This file contains directives, one per line.
|
|
|
+Leading and trailing whitespace and everything after the first # on
|
|
|
+any line will be trimmed, and empty lines will be ignored.
|
|
|
+
|
|
|
+The trigger control directives currently supported are:
|
|
|
+
|
|
|
+ interest <trigger-name>
|
|
|
+
|
|
|
+ Specifies that the package is interested in the named trigger.
|
|
|
+ All triggers in which a package is interested must be listed using
|
|
|
+ this directive in the triggers control file.
|
|
|
+
|
|
|
+ activate <trigger-name>
|
|
|
+
|
|
|
+ Arranges that changes to this package's state will activate the
|
|
|
+ specified trigger. The trigger will be activated at the start of
|
|
|
+ the following operations: unpack, configure, remove (including for
|
|
|
+ the benefit of a conflicting package), purge and deconfigure.
|
|
|
+
|
|
|
+ If this package disappears during the unpacking of another package
|
|
|
+ the trigger will be activated when the disappearance is noted
|
|
|
+ towards the end of the unpack. Trigger processing, and transition
|
|
|
+ from triggers-awaited to installed, does not cause activations.
|
|
|
+ In the case of unpack, triggers mentioned in both the old and new
|
|
|
+ versions of the package will be activated.
|
|
|
+
|
|
|
+Unknown directives are an error which will prevent installation of the
|
|
|
+package.
|
|
|
+
|
|
|
+Support future extension of the trigger name syntax with additional
|
|
|
+dpkg-generated triggers is as follows: a package which is interested
|
|
|
+in any unsupported trigger kinds cannot be configured (since such a
|
|
|
+package cannot be guaranteed to have these triggers properly activated
|
|
|
+by dpkg). Therefore no package can be interested in any unsupported
|
|
|
+trigger kinds and they can be freely activated (both by `activate' and
|
|
|
+by dpkg-trigger). dpkg-deb will be changed to warn about unrecognised
|
|
|
+trigger names syntaxes and unrecognised trigger control directives.
|
|
|
+
|
|
|
+
|
|
|
+New command-line interfaces to dpkg tools
|
|
|
+-----------------------------------------
|
|
|
+
|
|
|
+dpkg will grow new options:
|
|
|
+
|
|
|
+ --no-triggers
|
|
|
+ Do not run any triggers in this run (activations will still be
|
|
|
+ recorded). If used with dpkg --configure <some package> or
|
|
|
+ --triggers-only <some package> then the named package
|
|
|
+ postinst will still be run even if only a triggers run is needed.
|
|
|
+ --triggers
|
|
|
+ Cancels a previous --no-triggers.
|
|
|
+
|
|
|
+ --triggers-only
|
|
|
+ Processes only triggers. All pending triggers will be
|
|
|
+ processed. If package names are supplied only those packages'
|
|
|
+ triggers will be processed, exactly once each where necessary.
|
|
|
+
|
|
|
+Use of --no-triggers or --triggers-only may leave packages in the
|
|
|
+improper `triggers-awaited' and `triggers-pending' states. This can
|
|
|
+be fixed later by running:
|
|
|
+ dpkg --configure --pending
|
|
|
+
|
|
|
+Here is a summary of the behaviours:
|
|
|
+
|
|
|
+ Command line Trigproc Trigproc Configure
|
|
|
+ these any triggered
|
|
|
+ ----------------------+---------------+---------------+-----------------
|
|
|
+ --unpack no usually[1] none
|
|
|
+ --remove n/a usually[1] none
|
|
|
+ --install n/a usually[1] these
|
|
|
+ --configure -a any needed usually[1] any needed
|
|
|
+ --configure <some> if needed usually[1] must, or trigproc
|
|
|
+ --triggers-only -a any needed usually[1] none
|
|
|
+ --triggers-only <some> must usually not[1] none
|
|
|
+
|
|
|
+ [1] can be specified explicitly by --triggers or --no-triggers
|
|
|
+
|
|
|
+
|
|
|
+A trigger may be activated explicitly with:
|
|
|
+ dpkg-trigger [--by-package <package>] <name-of-trigger>
|
|
|
+ dpkg-trigger --no-await <name-of-trigger>
|
|
|
+
|
|
|
+This can be used by maintainer scripts in complex and conditional
|
|
|
+situations where the file triggers, or the declarative `activate'
|
|
|
+triggers control file directive, are insufficiently rich. It can also
|
|
|
+be used for testing and by system administrators (but note that the
|
|
|
+triggers won't actually be run by dpkg-trigger - see `Timing...',
|
|
|
+above).
|
|
|
+
|
|
|
+The --by-package option should not normally be necessary. dpkg will
|
|
|
+be modified to set an environment variable DPKG_MAINTSCRIPT_PACKAGE in
|
|
|
+the environment of maintainer scripts, naming the package to which the
|
|
|
+script belongs, and this will be used by default.
|
|
|
+
|
|
|
+The --no-await option arranges that the calling package T (if any)
|
|
|
+need not await the processing of this trigger; the interested
|
|
|
+package(s) I will not be added to T's trigger processing awaited list
|
|
|
+and T's status is unchanged. T may be considered installed even
|
|
|
+though I may not yet have processed the trigger.
|
|
|
+
|
|
|
+If a postinst would like to know whether the running dpkg supports
|
|
|
+triggers, it can ask
|
|
|
+ dpkg-trigger --check-supported
|
|
|
+which will exit 0 if a triggers-capable dpkg has run, or 1 with an
|
|
|
+error message to stderr if not. Normally, however, it is better just
|
|
|
+to activate the desired trigger with `dpkg-trigger'. See Transition
|
|
|
+Plan, below.
|
|
|
+
|
|
|
+The --verbose and --query options will show which packages were
|
|
|
+interested and what the current activation state is, on stdout in
|
|
|
+human- and machine-readable (untranslated) format. Without any
|
|
|
+options there will be no output to stdout, and none to stderr unless
|
|
|
+dpkg-trigger is unable to make a record of the trigger activation.
|
|
|
+With --query no trigger is activated.
|
|
|
+
|
|
|
+Unrecognised trigger name syntaxes are an error for dpkg-trigger.
|
|
|
+
|
|
|
+NB that in the case of a file trigger the name of the trigger is
|
|
|
+needed, not the name of a file which would match the trigger.
|
|
|
+
|
|
|
+
|
|
|
+apt and aptitude
|
|
|
+----------------
|
|
|
+
|
|
|
+These must be taught about the new `triggers-awaited' and
|
|
|
+`triggers-pending' states. Packages in these states should be treated
|
|
|
+roughly like those in `unpacked': the remedy is to run dpkg
|
|
|
+--configure.
|
|
|
+
|
|
|
+Normally apt and aptitude will not see packages in `triggers-pending'
|
|
|
+since dpkg will generally attempt to run the triggers thus leaving the
|
|
|
+package in `config-failed' or `installed'.
|
|
|
+
|
|
|
+Note that automatic package management tools which call dpkg (like apt
|
|
|
+and aptitude) should not attempt to configure individual packages in
|
|
|
+state `triggers-pending' (or indeed `triggers-awaited') with dpkg
|
|
|
+--triggers <package>... or dpkg --suppress-triggers --configure
|
|
|
+<package>..., or similar approaches. This might defeat dpkg's trigger
|
|
|
+cycle detection.
|
|
|
+
|
|
|
+A package management tool which will run dpkg --configure --pending at
|
|
|
+the end may use --suppress-triggers on its other dpkg runs. This
|
|
|
+would be more efficient as it allows more aggressive deferral (and
|
|
|
+hence more unification) of trigger processing.
|
|
|
+
|
|
|
+
|
|
|
+Error handling
|
|
|
+--------------
|
|
|
+
|
|
|
+Packages should be written so that they DO NOT BREAK just because
|
|
|
+their pending triggers have not yet been run. It is allowed for the
|
|
|
+functionality relating to the unprocessed trigger to fail (ie, the
|
|
|
+package which is awaiting the trigger processing may be broken), but
|
|
|
+the remainder of the interested package must work normally.
|
|
|
+
|
|
|
+For example, a package which uses file triggers to register addons
|
|
|
+must cope with (a) an addon being dropped into the filesystem but not
|
|
|
+yet registered and (b) an addon being removed but not yet
|
|
|
+deregistered. In both of these cases the package's main functionality
|
|
|
+must continue to work normally; failure of the addon in question is
|
|
|
+expected, warning messages are tolerable, but complete failure of the
|
|
|
+whole package, or failures of other addons, are not acceptable.
|
|
|
+
|
|
|
+dpkg cannot ensure that triggers are run in a timely enough manner for
|
|
|
+pathological error behaviours to be tolerable.
|
|
|
+
|
|
|
+
|
|
|
+Where a trigger script finds bad data provided by a triggering
|
|
|
+package, it should generally report to stderr the problem with the bad
|
|
|
+data and exit nonzero, leaving the interested package in config-failed
|
|
|
+and the triggering package in triggers-awaited and thus signalling the
|
|
|
+problem to the user.
|
|
|
+
|
|
|
+Alternatively, in some situations it may be more desirable to allow
|
|
|
+the interested package to be configured even though it can only
|
|
|
+provide partial service. In this case clear information will have to
|
|
|
+be given in appropriate places about the missing functionality, and a
|
|
|
+record should be made of the cause of the errors. This option is
|
|
|
+recommended for situations where the coupling between the interested
|
|
|
+and triggering package is particularly loose; an example of such a
|
|
|
+loose coupling would be Python modules.
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+WORKED EXAMPLE - SCROLLKEEPER
|
|
|
+=============================
|
|
|
+
|
|
|
+Currently, every Gnome program which comes with some help installs the
|
|
|
+help files in /usr/share/gnome/help and then in the postinst runs
|
|
|
+scrollkeeper-update. scrollkeeper-update reads, parses and rewrites
|
|
|
+some large xml files in /var/lib/scrollkeeper; currently this
|
|
|
+occurs at every relevant package installation, upgrade or removal.
|
|
|
+
|
|
|
+When triggers are available, this will work as follows:
|
|
|
+
|
|
|
+ * gnome-foobar will ship its `omf' file in /usr/share/omf as
|
|
|
+ normal, but will not contain any special machinery to invoke
|
|
|
+ scrollkeeper.
|
|
|
+
|
|
|
+ * scrollkeeper will in its triggers control file say:
|
|
|
+ interest /usr/share/omf
|
|
|
+ and in its postinst say:
|
|
|
+ scrollkeeper-update-now -q
|
|
|
+
|
|
|
+ dpkg will arrange that this is run once at the end of each run
|
|
|
+ where any documentation was updated.
|
|
|
+
|
|
|
+ Note that it is not necessary to execute this only on particular
|
|
|
+ postinst "$1" values; however, at the time of writing, scrollkeeper
|
|
|
+ does this:
|
|
|
+
|
|
|
+ if [ "$1" = "configure" ]; then
|
|
|
+ printf "Rebuilding the database. This may take some time.\n"
|
|
|
+ scrollkeeper-rebuilddb -q
|
|
|
+ fi
|
|
|
+
|
|
|
+ and to retain this behaviour, something along the following lines
|
|
|
+ would be sensible:
|
|
|
+
|
|
|
+ if [ "$1" = "configure" ]; then
|
|
|
+ printf "Rebuilding the database. This may take some time.\n"
|
|
|
+ scrollkeeper-rebuilddb -q
|
|
|
+ else
|
|
|
+ printf "Updating GNOME help database.\n"
|
|
|
+ scrollkeeper-update-now -q
|
|
|
+ fi
|
|
|
+
|
|
|
+ * dh_scrollkeeper will only adjust the DTD declarations and no longer
|
|
|
+ edit maintainer scripts.
|
|
|
+
|
|
|
+
|
|
|
+Full implementation of the transition plan defined below, for
|
|
|
+scrollkeeper, goes like this:
|
|
|
+
|
|
|
+ 1. Update scrollkeeper:
|
|
|
+ - Add a `triggers' control archive file containing
|
|
|
+ interest /usr/share/omf
|
|
|
+ - Make the postinst modifications as described above.
|
|
|
+ - Rename scrollkeeper-update to scrollkeeper-update-now
|
|
|
+ - Provide a new wrapper script as scrollkeeper-update:
|
|
|
+ #!/bin/sh -e
|
|
|
+ if type dpkg-trigger >/dev/null 2>&1 && \
|
|
|
+ dpkg-trigger /usr/share/omf; then
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+ exec scrollkeeper-update-now "$@"
|
|
|
+
|
|
|
+ 2. In gnome-policy chapter 2, `Use of scrollkeeper',
|
|
|
+ - delete the requirement that the package must depend on
|
|
|
+ scrollkeeper
|
|
|
+ - delete the requirement that the package must invoke
|
|
|
+ scrollkeeper in the postinst and postrm
|
|
|
+ - instead say:
|
|
|
+
|
|
|
+ OMF files should be installed under /usr/share/omf in the
|
|
|
+ usual way. A dpkg trigger is used to arrange to update the
|
|
|
+ scrollkeeper documentation index automatically and no special
|
|
|
+ care need be taken in packages which supply OMFs.
|
|
|
+
|
|
|
+ If an OMF file is placed, modified or removed other than as
|
|
|
+ an file installed in the ordinary way by dpkg, the dpkg file
|
|
|
+ trigger `/usr/share/omf' should be activated; see the dpkg
|
|
|
+ triggers specification for details.
|
|
|
+
|
|
|
+ Existing packages which Depend on scrollkeeper (>= 3.8)
|
|
|
+ because of dh_scrollkeeper or explicit calls to
|
|
|
+ scrollkeeper-update should be modified not to Depend on
|
|
|
+ scrollkeeper.
|
|
|
+
|
|
|
+ 3. Update debhelper's dh_scrollkeeper not to edit maintainer
|
|
|
+ scripts. One of dh_scrollkeeper or lintian should be changed to
|
|
|
+ issue a warning for packages with scrollkeeper (>= 3.8) in the
|
|
|
+ Depends control file line.
|
|
|
+
|
|
|
+ 4. Remove the spurious dependencies on scrollkeeper, at our leisure.
|
|
|
+ As a bonus, after this is complete it will be possible to remove
|
|
|
+ scrollkeeper while keeping all of the documentation-supplying
|
|
|
+ gnome packages installed.
|
|
|
+
|
|
|
+ 5. If there are any packages which do by hand what dh_scrollkeeper
|
|
|
+ does, change them not to call scrollkeeper-update and drop
|
|
|
+ their dependency on scrollkeeper.
|
|
|
+
|
|
|
+This is not 100% in keeping with the full transition plan defined
|
|
|
+below: if a new gnome package is used with an old scrollkeeper, there
|
|
|
+is some possibility that the help will not properly be available.
|
|
|
+
|
|
|
+Unfortunately, dh_scrollkeeper doesn't generate the scrollkeeper
|
|
|
+dependency in the control file, which makes it excessively hard to get
|
|
|
+the dependency up to date. The bad consequences of the inaccurate
|
|
|
+dependencies are less severe than the contortions which would be
|
|
|
+required to deal with the problem.
|
|
|
+
|
|
|
+
|
|
|
+TRANSITION PLAN
|
|
|
+===============
|
|
|
+
|
|
|
+
|
|
|
+Old dpkg to new dpkg
|
|
|
+--------------------
|
|
|
+
|
|
|
+The first time a trigger-supporting dpkg is run on any system, it will
|
|
|
+activate all triggers in which anyone is interested, immediately.
|
|
|
+
|
|
|
+These trigger activations will not be processed in the same dpkg run,
|
|
|
+to avoid unexpectedly processing triggers while attempting an
|
|
|
+unrelated operation. dpkg --configure --pending (and not other dpkg
|
|
|
+operations) will run the triggers, and the dpkg postinst will warn the
|
|
|
+user about the need to run it (if this deferred triggers condition
|
|
|
+exists). (Any triggers activated or reactivated *after* this
|
|
|
+mass-activation will be processed in the normal way.)
|
|
|
+
|
|
|
+To use this correctly:
|
|
|
+ * Packages which are interested in triggers, or which want to
|
|
|
+ explicitly activate triggers, should Depend on the
|
|
|
+ triggers-supporting version of dpkg.
|
|
|
+ * Update instructions and tools should arrange to run
|
|
|
+ dpkg --configure --pending
|
|
|
+ after the install; this will process the pending triggers.
|
|
|
+
|
|
|
+dpkg's prerm will check for attempts to downgrade while triggers are
|
|
|
+pending and refuse. (Since the new dpkg would be installed but then
|
|
|
+refuse to read the status file.) In case this is necessary a separate
|
|
|
+tool will be provided which will:
|
|
|
+ * Put all packages with any pending triggers into state
|
|
|
+ `config-failed' and remove the list of pending triggers.
|
|
|
+ * Remove the list of awaited triggers from every package. This
|
|
|
+ may cause packages to go from `triggers-awaited' to `installed'
|
|
|
+ which is not 100% accurate but the best that can be done.
|
|
|
+ * Remove /var/lib/dpkg/triggers (to put the situation to that which
|
|
|
+ we would have seen if the trigger-supporting dpkg had never been
|
|
|
+ installed).
|
|
|
+
|
|
|
+
|
|
|
+Higher-level programs
|
|
|
+---------------------
|
|
|
+
|
|
|
+The new dpkg will declare versioned Conflicts against apt and aptitude
|
|
|
+and other critical package management tools which will be broken by
|
|
|
+the new Status field values. Therefore, the new higher-level tools
|
|
|
+will have to be deployed first.
|
|
|
+
|
|
|
+The new dpkg will declare versioned Breaks against any known
|
|
|
+noncritical package management tools which will be broken by the new
|
|
|
+Status field value.
|
|
|
+
|
|
|
+
|
|
|
+Transition hints for existing packages
|
|
|
+--------------------------------------
|
|
|
+
|
|
|
+When a central (consumer) package defines a directory where other leaf
|
|
|
+(producer) packages may place files and/or directories, and currently
|
|
|
+the producer packages are required to run an `update-consumer' script
|
|
|
+in their postinst:
|
|
|
+ 1. In the relevant policy, define a trigger name which is the name of
|
|
|
+ the directory where the individual files are placed by producer
|
|
|
+ packages.
|
|
|
+ 2. Update the consumer package:
|
|
|
+ * Declare an interest in the trigger.
|
|
|
+ * Edit update-consumer so that if it is called without --real
|
|
|
+ it does the following:
|
|
|
+ if type dpkg-trigger >/dev/null 2>&1 && \
|
|
|
+ dpkg-trigger name-of-trigger; then
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+ If this fails to cause update-consumer to exit, it should do
|
|
|
+ its normal update processing. Alternatively, if it is more
|
|
|
+ convenient, update-consumer could be renamed and supplanted with
|
|
|
+ a wrapper script which conditionally runs the real
|
|
|
+ update-consumer.
|
|
|
+ * In the postinst, arrange for the new `triggered' invocation to
|
|
|
+ run update-consumer --real. The consumer package's postinst
|
|
|
+ will already run update-consumer during configuration, and this
|
|
|
+ should be retained and supplemented with the --real option (or
|
|
|
+ changed to call the real script rather than the wrapper).
|
|
|
+ 3. Update the producer packages:
|
|
|
+ * In the postinst, remove the call to update-consumer
|
|
|
+ * Change the dependency on consumer to be versioned, specifying a
|
|
|
+ trigger-interested consumer.
|
|
|
+ This can be done at our leisure. Ideally for loosely coupled
|
|
|
+ packages this would be done only in the release after the one
|
|
|
+ containing the triggers-interested consumer, to facilitate partial
|
|
|
+ upgrades and backports.
|
|
|
+ 4. After all producer packages have been updated according to step 3,
|
|
|
+ `update-consumer' has become an interface internal to the consumer
|
|
|
+ and need no longer be kept stable. If un-updated producers are
|
|
|
+ still of interest, incompatible changes to `update-consumer' imply
|
|
|
+ a versioned Breaks against the old producers.
|
|
|
+(See also `Transition plan', below.)
|
|
|
+
|
|
|
+If there are several consumer packages all of which are interested in
|
|
|
+the features provided by producer packages, the current arrangements
|
|
|
+usually involve an additional central switchboard package (eg,
|
|
|
+emacsen-common). In this case:
|
|
|
+
|
|
|
+ -- NOTE - this part of the transition plan is still a proof of
|
|
|
+ concept and we might yet improve on it
|
|
|
+
|
|
|
+ 1. Define the trigger name.
|
|
|
+ 2. Update the switchboard to have any new functionality needed by the
|
|
|
+ consumers in step 3 (2nd bullet).
|
|
|
+ 3. Update the consumer packages:
|
|
|
+ * Declare an interest in the trigger.
|
|
|
+ * In the postinst, arrange for the new `trigger' invocation to run
|
|
|
+ the compilation/registration process. This may involve scanning
|
|
|
+ for new or removed producers, and may involve new common
|
|
|
+ functionality from the switchboard (in which case a versioned
|
|
|
+ Depends is needed).
|
|
|
+ * The old interface allowing the switchboard to run
|
|
|
+ compilation/registration should be preserved, including
|
|
|
+ calls to the switchboard to register this consumer.
|
|
|
+ 4. When all consumers have been updated, update the switchboard:
|
|
|
+ * Make the registration scripts called by producers try to
|
|
|
+ activate the trigger and if that succeeds quit without
|
|
|
+ doing any work (as for bullet 2 in the simple case above).
|
|
|
+ * Versioned Breaks, against the old (pre-step-3) consumers.
|
|
|
+ 5. After the switchboard has been updated, producers can be updated:
|
|
|
+ * Remove the calls to the switchboard registration/compilation
|
|
|
+ functions.
|
|
|
+ * Change the dependency on the switchboard to a versioned one,
|
|
|
+ specifying the one which Breaks old consumers. Alternatively,
|
|
|
+ it may be the case that the switchboard is no longer needed (or
|
|
|
+ not needed for this producer), in which case the dependency on
|
|
|
+ the switchboard can be removed in favour of an appropriate
|
|
|
+ versioned Breaks (probably, identical to that in the new
|
|
|
+ switchboard).
|
|
|
+ 6. After all the producers have been updated, the cruft in the
|
|
|
+ consumers can go away:
|
|
|
+ * Remove the calls to the switchboard's registration system.
|
|
|
+ * Versioned Breaks against old switchboards, or versioned Depends
|
|
|
+ on new switchboards, depending on whether the switchboard is
|
|
|
+ still needed for other common functionality.
|
|
|
+ 7. After all of the producers and consumers have been updated, the
|
|
|
+ cruft in the switchboard can go away:
|
|
|
+ * Remove the switchboard's registration system (but not obviously
|
|
|
+ the common functionality from step 3, discussed above).
|
|
|
+ * Versioned Breaks against pre-step-6 consumers and pre-step-5
|
|
|
+ producers.
|
|
|
+
|
|
|
+
|
|
|
+DISCUSSION
|
|
|
+==========
|
|
|
+
|
|
|
+The activation of a trigger does not record details of the activating
|
|
|
+event. For example, file triggers do not inform the package of the
|
|
|
+filename. In the future this might be added as an additional feature,
|
|
|
+but there are some problems with this.
|
|
|
+
|
|
|
+
|
|
|
+Broken producer packages, and error reporting
|
|
|
+---------------------------------------------
|
|
|
+
|
|
|
+Often trigger processing will involve a central package registering,
|
|
|
+compiling or generally parsing some data provided by a leaf package.
|
|
|
+
|
|
|
+If the central package finds problems with the leaf package data it is
|
|
|
+usually more correct for only the individual leaf package to be
|
|
|
+recorded as not properly installed. There is not currently any way to
|
|
|
+do this and there are no plans to provide one.
|
|
|
+
|
|
|
+The naive approach of giving the postinst a list of the triggering
|
|
|
+packages does not work because this information is not recorded in the
|
|
|
+right way (it might suffer from lacunae); enhancing the bookkeeping
|
|
|
+for this to work would be possible but it is far better simply to make
|
|
|
+the system more idempotent. See above for the recommended approach.
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+INTERNALS
|
|
|
+=========
|
|
|
+
|
|
|
+On-disk state
|
|
|
+-------------
|
|
|
+
|
|
|
+A single file /var/lib/dpkg/triggers/File lists all of the filename
|
|
|
+trigger interests in the form
|
|
|
+ /path/to/directory/or/file package
|
|
|
+
|
|
|
+For each explicit trigger in which any package is interested,
|
|
|
+a file /var/lib/dpkg/triggers/<name-of-trigger> is a list of
|
|
|
+the interested packages, one per line.
|
|
|
+
|
|
|
+These interest files are not updated to remove a package just because
|
|
|
+a state change causes it not to be interested in any triggers any more
|
|
|
+- they are updated when we remove or unpack.
|
|
|
+
|
|
|
+For each package which has pending triggers, the status file contains
|
|
|
+a Triggers-Pending field which contains the space-separated names of
|
|
|
+the pending triggers. For each package which awaits triggers the
|
|
|
+status file contains a Triggers-Awaited field which contains the
|
|
|
+*package* names of the packages whose trigger processing is awaited.
|
|
|
+See `Details - Overview table' above for the invariants which relate
|
|
|
+Triggers-Pending, Triggers-Awaited, and Status.
|
|
|
+
|
|
|
+During dpkg's execution, /var/lib/dpkg/triggers/Unincorp is a list of
|
|
|
+the triggers which have been requested by dpkg-trigger but not yet
|
|
|
+incorporated in the status file. Each line is a trigger name followed
|
|
|
+by one or more triggering package names. The triggering package name
|
|
|
+"-" is used to indicate one or more package(s) which did not need to
|
|
|
+await the trigger.
|
|
|
+
|
|
|
+/var/lib/dpkg/triggers/Lock is the fcntl lockfile for the trigger
|
|
|
+system. Processes hang onto this lock only briefly: dpkg-trigger
|
|
|
+to add new activations, or dpkg to incorporate activations (and
|
|
|
+perhaps when it updates interests). Therefore this lock is always
|
|
|
+acquired with F_GETLKW so as to serialise rather than fail on
|
|
|
+contention.
|
|
|
+
|
|
|
+
|
|
|
+Processing
|
|
|
+----------
|
|
|
+
|
|
|
+dpkg-trigger updates triggers/Unincorp, and does not read or write the
|
|
|
+status file or take out the dpkg status lock. dpkg (and dpkg-query)
|
|
|
+reads triggers/Unincorp after reading /var/lib/dpkg/status, and after
|
|
|
+running a maintainer script. If the status database is opened for
|
|
|
+writing then the data from Unincorp is moved to updates as
|
|
|
+Triggers-Pending and Triggers-Awaited entries and corresponding Status
|
|
|
+changes.
|
|
|
+
|
|
|
+This means that dpkg is guaranteed to reincorporate pending trigger
|
|
|
+information into the status file only 1. when a maintainer script has
|
|
|
+finished, or 2. when dpkg starts up with a view to performing some
|
|
|
+operation.
|
|
|
+
|
|
|
+When a package is unpacked or removed, its triggers control file will
|
|
|
+be parsed and /var/lib/dpkg/triggers/* updated accordingly.
|
|
|
+
|
|
|
+Triggers are run as part of configuration. dpkg will try to first
|
|
|
+configure all packages which do not depend on packages which are
|
|
|
+awaiting triggers, and then run triggers one package at a time in the
|
|
|
+hope of making useful progress. (This will involve a new `dependtry'
|
|
|
+level in configure.c's algorithm.) The only constraint on the
|
|
|
+ordering of postinsts is only the normal Depends constraint, so the
|
|
|
+usual Depends cycle breaking will function properly. See `Cycle
|
|
|
+detection' below regarding cycles in the `A triggers B' relation.
|
|
|
+
|
|
|
+
|
|
|
+Processing - Transitional
|
|
|
+-------------------------
|
|
|
+
|
|
|
+The case where a triggers-supporting dpkg is run for the first time is
|
|
|
+detected by the absence of /var/lib/dpkg/triggers/Unincorp. When the
|
|
|
+triggers-supporting dpkg starts up without this it will set each
|
|
|
+package's list of pending triggers equal to its interests (obviously
|
|
|
+only for packages which are in `installed' or `triggers-pending').
|
|
|
+This may result in a package going from `installed' to
|
|
|
+`triggers-pending' but it will not create the directory at this time.
|
|
|
+Packages marked as triggers-pending in this way will not be scheduled
|
|
|
+for trigger processing in this dpkg run.
|
|
|
+
|
|
|
+dpkg will also at this time create /var/lib/dpkg/triggers if
|
|
|
+necessary, triggers/File, triggers/Unincorp, and the per-trigger
|
|
|
+package lists in /var/lib/dpkg/triggers/<trigger-name>, so that future
|
|
|
+trigger activations will be processed properly.
|
|
|
+
|
|
|
+Only dpkg may create /var/lib/dpkg/triggers and only when it is
|
|
|
+holding the overall dpkg status lock.
|
|
|
+
|
|
|
+dpkg and/or dpkg-deb will be made to reject packages containing
|
|
|
+Triggers-Pending and Triggers-Awaited control file fields, to prevent
|
|
|
+accidents.
|
|
|
+
|
|
|
+
|
|
|
+Cycle detection
|
|
|
+---------------
|
|
|
+
|
|
|
+In addition to dependency cycles, triggers raise the possibility of
|
|
|
+mutually triggering packages - a cycle detectable only dynamically,
|
|
|
+which we will call a `trigger cycle'.
|
|
|
+
|
|
|
+Trigger cycles are detected using the usual hare-and-tortoise
|
|
|
+approach. Each time after dpkg runs a postinst for triggers, dpkg
|
|
|
+records the set of pending triggers (ie, the set of activated <pending
|
|
|
+package, trigger name> tuples). If the hare set is a superset of the
|
|
|
+tortoise set, a cycle has been found.
|
|
|
+
|
|
|
+For guaranteed termination, it would be sufficient to declare a cycle
|
|
|
+only when the two sets are identical, but because of the requirement
|
|
|
+to make progress we can cut this short. Formally, there is supposed
|
|
|
+to be a complete ordering of pending trigger sets satisfying the
|
|
|
+condition that any set of pending triggers is (strictly) greater than
|
|
|
+all its (strict) subsets. Trigger processing is supposed to
|
|
|
+monotonically decrease the set in this ordering. (The set elements
|
|
|
+are <package, trigger name> tuples.)
|
|
|
+
|
|
|
+(See `Processing' above for discussion of dependency cycles.)
|
|
|
+
|
|
|
+--
|
|
|
+
|
|
|
+
|