123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814 |
- 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 (unless the
- trigger has been configured to not require it); 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 explicitly requested.
- │
- v
- ┌────────────┐
- │ unpacked │
- └─────┬──────┘
- │
- │
- (automatic)│ ┌───────────────┐
- │ │ config-failed │
- │ └─────┬─────────┘
- │ │ ^
- │ │ │
- ├──────<─────┘ │ ┌──────────────────────────────────┐
- │ (user request) │ │ triggers-pending │
- postinst │ │ │ or │
- "configure" │ │ │ triggers-awaited w/ some pending │
- │ │ └────────────┬─────────────────────┘
- │ │ │ ^
- ├────────>───────┤ postinst │ │
- │ error │ "triggered" │ │
- │ │ (automatic) │ │
- │ │ │ │ trigger(s)
- │ │ │ │ activated
- │ └────────<─────────┤ │
- │ error │ │
- │ │ │
- v v │
- ┌──────────────────────────────────────────────┴────┐
- │ installed or triggers-awaited w/ 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 occurred 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 optimize
- 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 " $2 " 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 minimize 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 the distribution 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 the distribution policy, so that a package which creates a
- relevant file in a maintainer script can activate the trigger explicitly.
- 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
- ---------------------------------------
- See deb-triggers(5).
- 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 unrecognized
- trigger names syntaxes and unrecognized trigger control directives.
- New command line interfaces to dpkg tools
- -----------------------------------------
- See dpkg(1).
- 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
- See dpkg-trigger(1).
- A trigger may be activated explicitly with:
- dpkg-trigger [--by-package <package>] <name-of-trigger>
- dpkg-trigger --no-await <name-of-trigger>
- There will be no output to stdout, and none to stderr unless
- dpkg-trigger is unable to make a record of the trigger activation.
- 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-only <package>... or dpkg --no-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 --no-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
- set -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
- a 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 serialize 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.)
|