From Fedora Project Wiki

Revision as of 19:18, 15 September 2009 by Toshio (talk | contribs) (scrollkeeper no longer needed)

RPM scriptlet recipes

Rpm spec files have several sections which allow packages to run code on installation and removal. These scriptlets are mostly used to update the running system with information from the package. This page offers a quick overview of the RPM scriptlets and a number of common recipes for scriptlets in packages. For a more complete treatment of scriptlets, please see the Maximum RPM book .

Contents:


Syntax

The basic syntax is similar to the %build, %install, and other sections of the rpm spec file. The scripts support a special flag, -p which allows the scriptlet to invoke a single program directly rather than having to spawn a shell to invoke the programs. (ie: %post -p /sbin/ldconfig)

The scriptlets also take an argument, passed into them by the controlling rpmbuild process. This argument, accessed via $1 is the number of packages of this name which will be left on the system when the action completes, except for %pretrans and %posttrans which are always run with $1 as 0 (%pretrans and %posttrans are available in rpm 4.4 and later). So for the common case of install, upgrade, and uninstall we have:

install upgrade uninstall
%pretrans $1 == 0 $1 == 0 (N/A)
%pre $1 == 1 $1 == 2 (N/A)
%post $1 == 1 $1 == 2 (N/A)
%preun (N/A) $1 == 1 $1 == 0
%postun (N/A) $1 == 1 $1 == 0
%posttrans $1 == 0 $1 == 0 (N/A)

Note that these values will vary if there are multiple versions of the same package installed (This mostly occurs with parallel installable packages such as the kernel. However, it can also occur when errors prevent a package upgrade form completing.) So it is a good idea to use this construct:

%pre
if [ $1 -gt 1 ] ; then
fi

...for %pre and %post scripts rather than checking that it equals 2.

Except in some really exceptional cases (if any), we want all scriptlets to exit with the zero exit status. Because rpm in its default configuration does not at the moment execute shell scriptlets with the -e argument to the shell, excluding explicit exit calls (frowned upon with a non-zero argument!), the exit status of the last command in a scriptlet determines its exit status. Most commands in the snippets in this document have a "|| :" appended to them, which is a generic trick to force the zero exit status for those commands whether they worked or not. Usually the most important bit is to apply this to the last command executed in a scriptlet, or to add a separate command such as plain ":" or "exit 0" as the last one in a scriptlet. Note that depending on the case, other error checking/prevention measures may be more appropriate, as well as running some commands only if we saw a previous command in the scriptlet which is a must prerequisite to succeed.

Non-zero exit codes from scriptlets break installs/upgrades/erases so that no further actions will be taken for that package in a transaction (see scriptlet ordering below), which may for example prevent an old version of a package from being erased on upgrades, leaving behind duplicate rpmdb entries and possibly stale, unowned files on the filesystem. There are some cases where letting the transaction to proceed when some things in scriptlets failed may result in partially broken setup. It is however often limited to that package only whereas letting a transaction to proceed with some packages dropped out on the fly is more likely to result in broader system wide problems.

Scriptlet Ordering

The scriptlets in %pre and %post are respectively run before and after a package is installed. The scriptlets %preun and %postun are run before and after a package is uninstalled. The scriptlets %pretrans and %posttrans are run at start and end of a transaction. On upgrade, the scripts are run in the following order:

  1. %pretrans of new package
  2. %pre of new package
  3. (package install)
  4. %post of new package
  5. %preun of old package
  6. (removal of old package)
  7. %postun of old package
  8. %posttrans of new package

Snippets

Shared libraries

Installing shared libraries requires running /sbin/ldconfig to update the dynamic linker's cache files. These can be invoked like:

%post
/sbin/ldconfig
%postun
/sbin/ldconfig

It is also common to invoke these with the '-p' option as they are often the only program invoked in a scriptlet:

%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig

If applicable, the latter way is recommended because doing so will automatically add appropriate dependencies on /sbin/ldconfig to the package (and FWIW, will prevent unnecessarily launching a shell process in the scriptlets).

Users and groups

These are discussed on a separate page

Services

Initscripts Conventions

Full guidelines for SysV-style initscripts can be found here: Packaging/SysVInitScript
Scriptlet specifics can be found here: Packaging/SysVInitScript#InitscriptScriptlets

GConf

GConf is a configuration scheme currently used by the GNOME desktop. Programs which use it setup default values in a [NAME] .schemas file which is installed under %{_sysconfdir}/gconf/schemas/[NAME] .schemas. These defaults are then registered with the gconf daemon which monitors the configuration values and alerts applications when values the applications are interested in change. The schema files also provide documentation about what each value in the configuration system means (which gets displayed when you browse the database in the gconf-editor program).

For packaging purposes, we have to disable schema installation during build, and also register the values in the [NAME] .schemas file with the gconf daemon on installation and unregister them on removal. Due to the ordering of the scriptlets, this is a four step process.

Disabling the GConf installation during the package creation can be done like so:

%install
rm -rf $RPM_BUILD_ROOT
export GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL=1
make install DESTDIR=$RPM_BUILD_ROOT
...

The GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL environment variable suppresses the installation of the schema during the building of the package. An alternative for some packages is to pass a configure flag:

%build
%configure --disable-schemas
...

Unfortunately, this configure switch only works if the upstream packager has adapted their Makefile.am to handle it. If the Makefile.am is not configured, this switch won't do anything and you'll need to use the environment variable instead.

Here's the second part:

Requires(pre): GConf2
Requires(post): GConf2
Requires(preun): GConf2
...
%pre
if [ "$1" -gt 1 ] ; then
export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
gconftool-2 --makefile-uninstall-rule \
%{_sysconfdir}/gconf/schemas/[NAME] .schemas >/dev/null || :
fi

In this section we uninstall the old schemas when we upgrade. The way we do this is first to get information about where gconf stores its values via the gconftool-2 --get-default-source line. Then we uninstall the schema from that source. If the package could be upgrading a package which had another name for the schema at one time, then we uncomment the lines to uninstall those as well.

The next section is for installing the new schema:

%post
export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
gconftool-2 --makefile-install-rule \
%{_sysconfdir}/gconf/schemas/[NAME] .schemas > /dev/null || :

Here we do the same things as in the %pre section for upgrading except the gconftool-2 switch used is --makefile-install-rule to install the new schemas instead of the uninstall-rule to remove the old schemas.

The last section deals with deleting the schemas on package removal:

%preun
if [ "$1" -eq 0 ] ; then
export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
gconftool-2 --makefile-uninstall-rule \
%{_sysconfdir}/gconf/schemas/[NAME] .schemas > /dev/null || :
fi

This snippet is nearly the same as the one for upgrading. Why can't we just combine this portion with the %pre portion? The answer is that we want to delete any old versions of the schema during an upgrade. But this has to happen before we install the new version (in the %post script) otherwise we end up removing the schema that the upgrading package installs. However, if it really is a removal that will leave no other instances of this package on the system, we have to clean up the schema before deleting it.

Note: RHEL4 suffers from GConf Bug #173869 . If you are building for EPEL-4, you need to add killall -HUP gconfd-2 > /dev/null || : after the gconftool-2 calls in all the scriptlets.

Texinfo

The GNU project and many other programs use the texinfo file format for much of its documentation. These info files are usually located in /usr/share/info/. When installing or removing a package, install-info from the info package takes care of adding the newly installed files to the main info index and removing them again on deinstallation.

Requires(post): info
Requires(preun): info
...
%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :

%preun
if [ $1 = 0 ] ; then
  /sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi

These two scriptlets tell install-info to add entries for the info pages to the main index file on installation and remove them at erase time. The "|| :" in this case prevents failures that would typically affect systems that have been configured not to install any %doc files, or have read-only mounted, %_netsharedpath /usr/share.

Scrollkeeper

In all current Fedora, rarian has replaced scrollkeeper. There is no scriptlet needed for rarian. For instructions on what to do in EPEL releases, see Packaging:EPEL#Scrollkeeper

desktop-database

Use this when a desktop entry has a 'MimeType key.

%post
update-desktop-database &> /dev/null || :

%postun
update-desktop-database &> /dev/null || :

Note: For FC5+, this scriptlet follows the same convention as mimeinfo files and gtk-icon-cache. Namely, the spec file should not Require desktop-file-utils for this. For older releases, one should

Requires(post): desktop-file-utils
Requires(postun): desktop-file-utils

(See http://bugzilla.redhat.com/180898 and http://bugzilla.redhat.com/180899)

mimeinfo

Use this when a package drops an XML file in %{_datadir}/mime/packages.

%post
update-mime-database %{_datadir}/mime &> /dev/null || :

%postun
update-mime-database %{_datadir}/mime &> /dev/null || :

Note that similarly to the gtk-update-icon-cache code, these scriptlets should be run only if the user has update-mime-info installed and without a specific Requires: shared-mime-info. If shared-mime-info is not installed, update-mime-database won't be run when this package is installed. This does not matter because it will be run when the shared-mime-info package is installed.

Icon Cache

If an application installs icons into one of the subdirectories in %{_datadir}/icons/ (such as hicolor in the following examples), icon caches must be updated so that the installed icons show up in menus right after package installation. This consists of updating the timestamp of the top-level icon directory where the icons were installed, and running gtk-update-icon-cache. 'touch'ing the top-level dir is done so that environments compatible with the Icon theme specification can refresh their caches, and gtk-update-icon-cache which is additionally required for GNOME also does its work based on the dir timestamp.

Note that no dependencies should be added for this. If gtk-update-icon-cache is not available, there's nothing that would be needing the cache update, ditto if "touch" is not available, there's nothing that would benefit from icon cache updates installed yet either. Not adding the dependency on gtk-update-icon-cache (ie. gtk2 >= 2.6.0) or "touch" makes it easier to use the package (or the same specfile) on systems where it's not available nor needed, such as older distro versions or (very) trimmed down installations, and generally results in less entries in specfiles, rpmdb, and repo metadatas.

%post
touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :

%postun
if [ $1 -eq 0 ] ; then
    touch --no-create %{_datadir}/icons/hicolor &>/dev/null
    gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
fi

%posttrans
gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :