13.3. Scripts: RPM's Workhorse

The scripts that RPM uses to control the build process are among the most varied and interesting parts of the spec file. Many spec files also contain scripts that perform a variety of tasks whenever the package is installed or erased.

The start of each script is denoted by a keyword. For example, the %build keyword marks the start of the script RPM will execute when building the software to be packaged. It should be noted that, in the strictest sense of the word, these parts of the spec file are not scripts. For example, they do not start with the traditional invocation of a shell. However, the contents of each script section are copied into a file and executed by RPM as a full-fledged script. This is part of the power of RPM: Anything that can be done in a script can be done by RPM.

Let's start by looking at the scripts used during the build process.

13.3.1. Build-time Scripts

The scripts that RPM uses during the building of a package follow the steps known to every software developer:

Although each of the scripts perform a specific function in the build process, they share a common environment. Using RPM's --test option [1] , we can see the common portion of each script. In the following example, we've taken the cdplayer package, issued an rpm -ba --test cdplayer-1.0-1.spec, and viewed the script files left in RPM's temporary directory. This section (with the appropriate package-specific values) is present in every script RPM executes during a build:

#!/bin/sh -e
# Script generated by rpm

RPM_SOURCE_DIR="/usr/src/redhat/SOURCES"
RPM_BUILD_DIR="/usr/src/redhat/BUILD"
RPM_DOC_DIR="/usr/doc"
RPM_OPT_FLAGS="-O2 -m486 -fno-strength-reduce"
RPM_ARCH="i386"
RPM_OS="Linux"
RPM_ROOT_DIR="/tmp/cdplayer"
RPM_BUILD_ROOT="/tmp/cdplayer"
RPM_PACKAGE_NAME="cdplayer"
RPM_PACKAGE_VERSION="1.0"
RPM_PACKAGE_RELEASE="1"
set -x

umask 022
          

As we can see, the script starts with the usual invocation of a shell (in this case, the Bourne shell). There are no arguments passed to the script. Next, a number of environment variables are set. Here's a brief description of each one:

All of these environment variables are set for your use, to make it easier to write scripts that will do the right thing even if the build environment changes.

The script also sets an option that causes the shell to print out each command, complete with expanded arguments. Finally, the default permissions are set. Past this point, the scripts differ. Let's look at the scripts in the order they are executed.

13.3.2. Install/Erase-time Scripts

The other type of scripts that are present in the spec file are those that are only used when the package is either installed or erased. There are four scripts, each one meant to be executed at different times during the life of a package:

Unlike the build-time scripts, there is little in the way of environment variables for these scripts. The only environment variable available is RPM_INSTALL_PREFIX, and that is only set if the package uses an installation prefix.

Unlike the build-time scripts, there is an argument defined. The sole argument to these scripts, is a number representing the number of instances of the package currently installed on the system, after the current package has been installed or erased. Sound tricky? It really isn't. Here's an example:

Assume that a package, called blather-1.0, is being installed. No previous versions of blather have been installed. Since the software is being installed, only the %pre and %post scripts are executed. The argument passed to these scripts will be 1, since the the number of blather packages installed is 1. [3]

Continuing our example, a new version of the blather package, version 1.3, is now available. Clearly it's time to upgrade. What will the scripts' values be during the upgrade? As blather-1.3 is installing, its %pre and %post scripts will have an argument equal to 2 (1 for version 1.0 already installed, plus 1 for version 1.3 being installed). As the final part of the upgrade, it's then time to erase blather version 1.0. As the package is being removed, its %preun and %postun scripts are executed. Since there will be only one blather package (version 1.3) installed after version 1.0 is erased, the argument passed to version 1.0's scripts is 1.

To finally bring an end to this example, we've decided to erase blather 1.3. We just don't need it anymore. As the package is being erased, its %preun and %postun scripts will be executed. Since there will be no blather packages installed once the erase completes, the argument passed to the scripts is 0.

With all that said, of what possible use would this argument be? Well, it has two very interesting properties:

  1. When the first version of a package is installed, its %pre and %post scripts will be passed an argument equal to 1.

  2. When the last version of a package is erased, its %preun and %postun scripts will be passed an argument equal to 0.

Based on these properties, it's trivial to write an install-time script that can take certain actions in specific circumstances. Usually, the argument is used in the %preun or %postun scripts to perform a special task when the last instance of a package is being erased.

What is normally done during these scripts? The exact tasks may vary, but in general, the tasks are any that need to be performed at these points in the package's existence. One very common task is to run ldconfig when shared libraries are installed or removed. But that's not the only use for these scripts. It's even possible to use the scripts to perform tests to ensure the package install/erasure should proceed.

Since each of these scripts will be executing on whatever system installs the package, it's necessary to choose the script's choice of tools carefully. Unless you're sure a given program is going to be available on all the systems that could possibly install your package, you should not use it in these scripts.

13.3.3. Verification-Time Script — The %verifyscript Script

The %verifyscript executes whenever the installed package is verified by RPM's verification command. The contents of this script is entirely up to the package builder, but in general the script should do whatever is necessary to verify the package's proper installation. Since RPM automatically verifies the existence of a package's files, along with other file attributes, the %verifyscript should concentrate on different aspects of the package's installation. For example, the script may ensure that certain configuration files contain the proper information for the package being verified:

for n in ash bsh; do
    echo -n "Looking for $n in /etc/shells... "
    if ! grep "^/bin/${n}\$" /etc/shells > /dev/null; then
        echo "missing"
        echo "${n} missing from /etc/shells" >&2
    else
        echo "found"
    fi
done
          

In this script, the config file /etc/shells, is checked to ensure that it has entries for the shells provided by this package.

It is worth noting that the script sends informational and error messages to stdout, and error messages only to stderr. Normally RPM will only display error output from a verification script; the output sent to stdout is only displayed when the verification is run in verbose mode.

Notes

[1]

Described in Section 12.1.11.

[2]

Keep in mind that this command in a %clean script can wreak havoc if used with a build root of, say, /. Section 12.1.13.1 discusses this in more detail.

[3]

Or it will be 1, once the package is completely installed. Remember, the number is based on the number of packages installed after the current package's install or erase has completed.