Draft spec for new dpkg "triggers" feature

Ian Jackson iwj at ubuntu.com
Tue Jan 30 17:15:44 GMT 2007

For some time the idea of having some kind of event queue notification
mechanism in dpkg has been floating about.  For example, it would be
used to avoid running scrollkeeper-update dozens of times during an
upgrade and could simplify the emacs addon registration.  Wichert and
I even had a good design conversation in a taxi some years ago but we
failed to make proper notes and write it up.

I hope to be able to implement such a feature at some point in the
nearish future.  To this end I've written a draft specification which
you can find below.  If you're interested, please read it and comment.

Note the wide crosspost of this message.  I have set followups to go
to debian-dpkg, which is probably the right place for this discussion.




A dpkg trigger is a facility that allows events of interest to a
package to be recorded and aggregated, and processed later by that
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.)


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
 * 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
 * Special triggers, which activate magic code in dpkg itself.
   Currently none of these are defined.

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.

A package with pending triggers is not considered properly installed
until efforts to notify it of the trigger event have been successful.
The new state of having pending triggers is a dpkg package status of
`triggered', which lies somewhere between `config-failed' and


When one of the triggers in which a package is interested is
activated, the package goes from state `installed' to state
`triggered'; the list of pending triggers for each package is

If no package is interested in a trigger then activations are ignored;
this is not an error.

To restore a package in state `triggered' to `installed', dpkg will
run the postinst script:
   postinst triggered "<trigger-name> <trigger-name> ..."

This will be attempted for each package in state `triggered' at the
end of each dpkg run; so, normally, in the same dpkg run as the event
which made the package go to `triggered'.  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.

       |  unpacked  |
  (automatic)|     ,----------.
     	     |     | config-  |
     	     |     |  failed  |
     	     |     `----------'
     	     |	     |      ^
     	     |	     |      |
     	     |,---<--'      |
     	     | 	(user       |
    postinst |	 request)   |
 "configure" |              |               ,-----------.
	     |              |               | triggered |
	     |              |               `-----------'
	     |`----->------'|                  |    ^
	     |	  error     |        postinst  |    |
	     |              |      "triggered" |    | trigger(s)
	     |              |      (automatic) |    |  activated
	     |              |                  |    |
	     |              `-----<-----------'|    |
	     |			error	       |    |
	     |				       |    |
	     V				       V    |
       |   installed					|

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 during the
period from when the trigger is activatated 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.

If the package is not in state `installed' then it not interested,
even if it declares some trigger interests; when a package is
deconfigured for any reason, its list of pending triggers is reset.

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, 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.

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 activating
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
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 modifed at times other than package management,
should not be used for file triggers; 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 activating 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.

Because of the restriction on trigger names, it is not possible to
declare a file trigger for a directory whose name contains
whitespace.  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 is trimmed and lines starting with #
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.

    Trigger names not matching any currently supported trigger kind
    name syntax are ignored for `interest'.  This will allow future
    extensions (see TRANSITION PLAN, below).

 activate <trigger-name>

    Arranges that any change of the package's state will activate the
    specified trigger.  The trigger will be activated just before the
    package state changes.

Unknown directives, or unknown trigger name syntaxes for `activate',
are an error which will prevent installation of the package.  dpkg-deb
will be changed to to warn about unrecognised trigger names syntaxes
and unrecognised trigger control directives.

New command-line interfaces to dpkg tools

dpkg will grow new options:

      Do not run any triggers in this run (activations will still be
      recorded).  If used with dpkg --configure <some package> or
      --process-triggers <some package> then the named package
      postinst will still be run even if only a triggers run is
      Cancels a previous --suppress-triggers.

      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 --suppress-triggers, or an explicit list of packages with
--triggers, may leave packages in the improper `triggers' state.  This
can be fixed later by running one of:
   dpkg --configure --pending
   dpkg --triggers

A trigger may be activated explicitly with:
   dpkg-trigger [--verbose|--query] <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...',

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 `triggered' state.  Packages in
`triggered' should be treated roughly like those in `unpacked': the
remedy is to run dpkg --configure.

However, normally apt and aptitude will not see packages in
`triggered' 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' with dpkg --triggers <package>... or dpkg
--suppress-triggers --configure <package>..., or similar approaches.
This would defeat dpkg's trigger cycle detection.

Error handling: bogus data found by trigger scripts

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 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.  However, this
option is not normally recommended.


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 will in its triggers control file say:
       interest /usr/share/omf
   and in its postinst say:
       scrollkeeper-update -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

   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
         printf "Updating GNOME help database.\n"
	 scrollkeeper-update -q

 * 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
     - Declare a versioned dependency on the triggers-supporting
     - Make the postinst modifications as described above.

 2. In gnome-policy chapter 2, `Use of scrollkeeper',
     - delete the requirement that the package must depend on
     - 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

 3. Update debhelper's dh_scrollkeeper not to edit maintainer
    scripts.  dh_scrollkeeper should also issue a warning if it finds
    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 change their
    dependency to refer to the triggers-interested scrollkeeper
    version, or delete the dependency entirely.

This 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.  Likewise,
if a new gnome package is built with an old debhelper, its postinst
will call scrollkeeper-update but no dependency on scrollkeeper will
be present.

However, dh_scrollkeeper doesn't generate the scrollkeeper dependency
in the control file, which makes it excessively hard to get the
dependency up to date.  (This was a mistake in the design of
dh_scrollkeeper.)  The bad consequences of these missing dependencies
are less severe than the contortions which would be required to deal
with the problem.


More information about the ubuntu-devel mailing list