From Fedora Project Wiki

< Tools‎ | RPM

Internal RPM State Machines

When updating a system RPM walks through three state machines:

  • TSM (Transaction State Machine)
  • PSM (Package State Machine)
  • FSM (File State Machine)

These state machines are chained such that the TSM enters the PSM, and the PSM enters the FSM. The TSM is responsible for the overall transaction of packages, and calls the PSM to install/erase/repackage individual packages. In turn the PSM is responsible for managing the install/erasure/repackaging of individual packages and it calls the FSM to handle the files of an individual package.

Transaction State Machine

As stated above the transaction state machine is responsible for the overall updating of the system via an rpm transaction. Each transaction (an rpmts) contains various install elements and erase elements (these are rpmte's in the source). Unlike the PSM and the FSM, the TSM does not have programatic states that it goes through (i.e. its not really coded as a state machine) but its very helpful to think of it in terms of state machine. The source files that comprise things relating to the TSM are:

  • rpmte.c, rpmte.h - Defines methods and attributes of transaction elements.
  • rpmts.c, rpmts.h - Defines methods and attributes of transactions.
  • transaction.c - Defines high level actions that can be perfomed upon an rpmts/transaction.

Depending on how you want to think about it, you can consider the transaction state machine as having several entry points that must be manually entered (rpmtsCheck(), rpmtsOrder(), rpmtsRun()) in order, or you can think of rpmtsCheck and rpmtsOrder as just setup for the entry of the TSM via rpmtsRun(). If you take the first approach, then state machine has a high level logic of:

Check Transaction For Dependency Satisfaction (rpmtsCheck())
If success
Order Transaction Elements based on dependencies (rpmtsOrder())
if success
Run Transaction (rpmtsRun())

Either way you look at it the major work of the TSM is done in rpmtsRun, so below is pseudo code to document its flow:

Not Done Yet

The Package State Machine

The PSM is called at various times by the TSM for one of three purposes:

  • to install a package (a transaction element from the view point of the TSM).
  • to erase a package.
  • to repackage a package that will be erased.

These "purposes" are seen by the PSM as an overall goal to achieve, which are defined by the following macros:

  • PSM_PKGINSTALL - used to install a package.
  • PSM_PKGERASE - used to erase a package.
  • PSM_PKGSAVE - used to repackage a package.

The PSM is thus entered with one of these three initial states, that then gets translated to the PSM goal. Whichever the goal the PSM at a high level looks like this:

PSM_INIT                                     # Initialize package state machine for goal
if success PSM_PRE                   # Pre package install/erase/repackage activites (e.g. %pre)
if success PSM_PROCESS               # Deliver/Erase/Repackage Files (FSM is entered here).
if success PSM_POST                  # Post package install/erase/repackage activities (e.g. %post)
PSM_FINI                                     # Clean up PSM

To enter into the PSM, a call is made to rpmpsmStage() (this is found in psm.c) with the second argument being the state/stage you wish the PSM to transition too. The intial stage is, is one of the three states listed above (i.e. PSM_PKGINSTALL, PSM_PKGERASE, PSM_PKGSTAGE). After this initial entry the PSM transitions through the five major states listed above (i.e. PSM_INIT, PSM_PRE, PSM_PROCESS, PSM_POST, PSM_FINI). Beyond these major states, their are several sub states into which each of the major states can transition. They are listed below:

  • PSM_COMMIT - NOT SURE
  • PSM_CHROOT_IN - Enter chroot environment.
  • PSM_CHROOT_OUT - Exit chroot environment.
  • PSM_SCRIPT - Run a scriptlet.
  • PSM_TRIGGERS - Run triggers that are set against this package.
  • PSM_IMMED_TRIGGERS - Run triggers owned by this package.
  • PSM_RPMIO_FLAGS - Setup rpmio flags.
  • PSM_RPMDB_LOAD - Retrieves installed package header.
  • PSM_RPMDB_ADD - Adds header from package to rpmdb.
  • PSM_RPMDB_REMOVE - Removes package header from rpmdb.

The following are in the PSM, but are no-ops presently (Jeff, Is there an explanation for these? Well, I know there is, but is this dead code, or code waiting to be implemented:

  • PSM_PKGCOMMIT - Not used (Jeff could you elaborate on this?)
  • PSM_UNDO - Not used (Jeff, ditto)
  • PSM_CREATE - Not used.
  • PSM_DESTROY - Not used.
  • PSM_NOTIFY - Used by the PSM to notify consumers of librpm of package events.

The remaining subsections will list the high level logic of the major states (PSM_INIT, PSM_PRE, PSM_PROCESS, PSM_POST, PSM_FINI).

PSM_PKGINSTALL, PSM_PKGERASE, PSM_PKGSAVE

set PSM goal
transition to PSM_INIT
if OK
transition to PSM_PRE
if OK
transition to PSM_PROCESS
if OK
transition to PSM_POST
transition to PSM_FINI

PSM_INIT

calculate pkg instance count
if goal PSM_PKGINSTALL
increment pkg instance count
Try to find matching NEVRAO header in rpmdb and save in PSM state.
if JUSTDB  return
if file count is <= 0 return
strip PREFIX from files in old format relocatable packages?
strip / from files in current format relocatable packages

if goal PSM_PKGERASE
decrement pkg instance count
retrieve pkg header from rpmdb
if goal PSM_PKGSAVE
generate path to repackaged package
open repackaged package for write

return

PSM_PRE

if test return
transition to PSM_CHROOT_IN   # This will change into the chroot, if we are not already there; then it returns here
if goal PSM_PKGINSTALL
#
if %pre allowed
setup PSM to to run %pre script
transition to PSM_SCRIPT      # Will run %pre script
if goal PSM_PKGERASE
#
if %preuntrigger allowed
setup PSM to run %preuntriggers
transition to PSM_IMMED_TRIGGERS  # runs %preuntriggers owned by this package
transition to PSM_TRIGGERS               # runs %preuntriggers triggered by this package
#
if %preun allowed
setup PSM to run %preun
transition to PSM_SCRIPT                   # runs %pre and returns here
if goal PSM_PGKSAVE
get package header from rpmdb
transition to PSM_RPMIO_FLAGS
write package lead into repackaged package
write package signature into repackaged package
#
add REMOVETID to header
write header to repackaged package

PSM_PROCESS

if test return
if goal PSM_PKGINSTALL
if justdb return
if no files in package
synthesize notify callbacks       # Will result in progress hashes being printed
return
iterate over files in package
if user of file does not exit on the system
turn off SUID bit of file
set user of file to 0
if group of file does not exist on the system
turn off the SGID bit
set group of file to 0
transition to PSM_RPMIO_FLAGS   # sets compression type
#
if fd for package does not exist return FAILURE
#
if cfd for package does not exist return FAILURE
enter FSM with goal of FSM_PKGINSTALL
if FSM suceeded
transition to PSM_COMMIT
#
transition to PSM_NOTIFY with RPMCALLBACK_INST_PROGRESS
if FSM failed
print RPM error
transition to PSM_NOTIFY with RPMCALLBACK_UNPACK_ERROR
return FAILURE
return OK
if goal PSM_PKGERASE
if justdb return OK
if apply only return OK   # ???
if no files to erase return OK
transition to PSM_NOTIFY with RPMCALLBACK_UNINST_START
enter FSM with goal of FSM_PKGERASE  # This will remove any files remaining owned only by this package
transition to PSM_NOTIFY with RPMCALLBACK_UNINST_STOP
return OK
if goal PSM_PKGSAVE
set fileset action to FA_COPYOUT # ???
set fileset actions to NULL
if no fd for package return FAIL
if no cfd for package return FAIL # ???
enter FSM with goal of FSM_PKGBUILD
transition to PSM_NOTIFY with RPMCALLBACK_INST_PROGRESS
return OK

PSM_POST

if test return OK
if goal PSM_PKGINSTALL
calculate install time
set RPMTAG_FILESTATES in header      # ???
set RPMTAG_INSTALLTIME in header
set RPMTAG_INSTALLCOLOR in header
#
transition to PSM_RPMDB_REMOVE
#
transition to PSM_RPMDB_ADD
if %post allowed
transition to PSM_SCRIPT  # Run %post script
if %triggerin allowed
transition to PSM_TRIGGERS                # Run %triggerin in other packages
transition to PSM_IMMED_TRIGGERS   # Run %triggerin in this package
if not apply only
mark replaced files  # ???
if goal PSM_PKGERASE
if %postun allowed
transition to PSM_SCRIPT    # Run %postun
if %triggerpostun allowed
transition to PSM_TRIGGERS   # Run %triggerpostun in other packages
if not apply only
transition to PSM_RPMDB_REMOVE   # Remove header from db
#
transition to PSM_CHROOT_OUT
return OK

PSM_FINI

#
transition to PSM_CHROOT_OUT
close any package fd
if PSM has had error
display error message
transition to PSM_NOTIFY with RPMCALLBACK_CPIO_ERROR
clean up

The File State Machine

NOTHING YET

Acknowledgements

This information originally came from James Olin Oden's personal examination of the rpm source code.