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:
- %pretrans of new package
- %pre of new package
- (package install)
- %post of new package
- %triggerin of other packages (set off by installing new package)
- %triggerin of new package (if any are true)
- %triggerun of old package (if it's set off by uninstalling the old package)
- %triggerun of other packages (set off by uninstalling old package)
- %preun of old package
- (removal of old package)
- %postun of old package
- %triggerpostun of old package (if it's set off by uninstalling the old package)
- %triggerpostun of other packages (if they're setu off by uninstalling the old package)
- %posttrans of new package
Snippets
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 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:
BuildRequires: GConf2 Requires(pre): GConf2 Requires(post): GConf2 Requires(preun): GConf2 ... %pre %gconf_schema_prepare schema1 schema2 %gconf_schema_obsolete schema3
In this section we uninstall old schemas during upgrade using one of two macros.
%gconf_schema_prepare
is used for any current GConf schemas. It
takes care of uninstalling previous versions of schemas that this package
currently installs. It takes a space separated list of schema names without
path or suffix that the package installs. Note that behind the scenes, this
macro works with the %post
scriptlet to only process GConf schemas
if changes have occurred.
%gconf_schema_obsolete
is used for schemas that this package
previously provided but no longer does. It will deregister the old schema if
it is present on the system. Nothing will happen if the old schema is not
present. This macro takes a space separated list of schemas to uninstall. One
example of using this might be if the package changed names. If the old schema
was named foo.schemas
and the new schema is named
foobar.schemas
you'd use:
%gconf_schema_prepare foobar %gconf_schema_obsolete foo
The next section does the processing of the newly installed schemas:
%post %gconf_schema_upgrade schema1 schema2
%gconf_schema_upgrade
takes a space separated list of schemas that
the package currently installs just like %gconf_schema_prepare
.
Behind the scenes, it does the actual work of registering the new version of
the schema and deregistering the old version.
The last section is for unregistering schemas when a package is removed:
%preun %gconf_schema_remove schema1 schema2
When a package is upgraded rpm invokes the %pre
scriptlet to register
and deregister the schemas. When a package is uninstalled, the
%preun
scriptlet is used. %gconf_schema_remove
takes
the list of schemas that this package currently provides and removes them for us.
Rebuilds for changes to macros
When macros change, packages that make use of them have to be rebuilt to pick up the changes. This repoquery command can be used to find the schema including packages to rebuild:
repoquery --whatprovides "/etc/gconf/schemas/*" |sort |uniq |wc -l
EPEL Notes
EPEL does not have macros.gconf2, so please follow the instructions found here: Packaging:EPEL#GConf
GSettings Schema
GSettings is the configuration system used by the GNOME 3 desktop. It replaces the older GConf system, which was used in GNOME 2. GSettings has pluggable backends, the 'native' one for GNOME is using DConf to store settings. The GSettings API and utilities are part of the glib2 package.
Programs which use GSettings install schema information including default values in the directory %{_datadir}/glib-2.0/schemas. Schema files are xml files with the extension .gschema.xml. At runtime, GSettings uses the schemas in a compiled binary (but arch-neutral) form, which is created by running the glib-compile-schemas utility. glib-compile-schemas must be run whenever the set of installed schemas changes.
%postun if [ $1 -eq 0 ] ; then glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : fi %posttrans glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
gdk-pixbuf loaders
gdk-pixbuf is a library that is part of the gdk-pixbuf2 package. It is for loading images in various formats in GNOME. gdk-pixbuf can be extended by implementing loaders for image formats in loadable modules. These loadable modules have to be installed in %{_libdir}/gdk-pixbuf-2.0/2.10.0/loaders. To avoid opening all modules in that directory unnecessarily, gdk-pixbuf maintains a cache with information about the available modules in the text file %{_libdir}/gdk-pixbuf-2.0/2.10.0/loaders.cache. This cache file needs to be updated when the set of installed modules changes, by calling the gdk-pixbuf-query-loaders binary. Multilib considerations force us to install the binary in -32 and -64 variants.
The scriptlets to maintain the cache file are:
%postun gdk-pixbuf-query-loaders-%{__isa_bits} --update-cache &> /dev/null || : %post if [ $1 -eq 1 ] ; then # For upgrades, the cache will be regenerated by the new package's %postun gdk-pixbuf-query-loaders-%{__isa_bits} --update-cache &> /dev/null || : fi
Note the use of %{__isa_bits}, which is an rpm macro that expands to either 32 or 64, depending on the architecture of the package.
GTK+ modules
The GTK+ toolkit (in the gtk3 package) can be extended by loadable modules which can provide theme engines, input methods, print backends or other functionality. These modules have to be installed in subdirectories of %{_libdir}/gtk-3.0 or %{_libdir}/gtk-3.0/3.0.0. For the input methods, GTK+ maintains a cache in the text file %{_libdir}/gtk-3.0/3.0.0/immodules.cache. This cache file needs to be updated when the set of installed input methods changes, by calling the gtk-query-immodules-3.0 binary. Multilib considerations force us to install the binary in -32 and -64 variants.
The scriptlets to maintain the cache file are:
%postun gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : %post if [ $1 -eq 1 ] ; then # For upgrades, the cache will be regenerated by the new package's %postun gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : fi
The 3.0 in the binary name is there because gtk2 has its own utility for the same purpose, called gtk-query-immodules-2.0. Note the use of %{__isa_bits}, which is an rpm macro that expands to either 32 or 64, depending on the architecture of the package.
GIO modules
GIO is a library that is part of the glib2 package. It is a low-level part of the GNOME stack. GIO can be extended by implementing extension points in loadable modules. These loadable modules have to be installed in %{_libdir}/gio/modules. To avoid opening all modules in that directory unnecessarily, GIO maintains a cache with information about the available modules in the text file giomodule.cache in the same directory. This cache file needs to be updated when the set of installed modules changes, by calling the gio-querymodules binary. Multilib considerations force us to install the binary in -32 and -64 variants.
The scriptlets to maintain the cache file are:
%postun gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : %post if [ $1 -eq 1 ] ; then # For upgrades, the cache will be regenerated by the new package's %postun gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules || : fi
Note the use of %{__isa_bits}, which is an rpm macro that expands to either 32 or 64, depending on the architecture of the package.
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 || :
Systemd
Packages containing systemd unit files need to use scriptlets to ensure proper handling of those services. Services can either be enabled or disabled by default. To determine which case your specific service falls into, please refer to FESCo's policy here: Starting_services_by_default. On upgrade, a package may only restart a service if it is running; it may not start it if it is off. Also, the service may not enable itself if it is currently disabled.
There are two sets of scriptlets to use
New Packages
Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units [...] %post if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi %preun if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable apache-httpd.service > /dev/null 2>&1 || : /bin/systemctl stop apache-httpd.service > /dev/null 2>&1 || : fi %postun /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart apache-httpd.service >/dev/null 2>&1 || : fi
Note that /bin/systemctl daemon-reload will automatically detect new systemd unit .service files placed into %{_unitdir}. There is no equivalent to 'chkconfig --add <service>', because it is unnecessary. However, if the service is not enabled by default, systemctl will not list your service until it is either manually started (systemctl start foo.service
) or manually enabled (systemctl enable foo.service
).
If your service should be enabled by default, then use the following %post
scriptlet instead of the one shown above:
%post if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl enable apache-httpd.service >/dev/null 2>&1 || : fi
Packages migrating to a systemd unit file from a SysV initscript
When updating from a package containing a SysV initscript to a package containing a systemd unit file, we "start-over fresh" with default start and stop policy from the new package, and do not migrate what the user had previously configured. systemd provides a tool (systemd-sysv-convert --apply
) to help do this conversion if the user wants after the package is updated.
Packages in this scenario need to include all of the above scriptlets from "New Packages" and also add the additional scriptlets below:
# This is actually needed for the %triggerun script but Requires(triggerun) # is not valid. We can use %post because this particular %triggerun script # should fire just after this package is installed. Requires(post): systemd-sysv ### A sysv => systemd migration contains all of the same scriptlets as a ### systemd package. These are additional scriptlets # Note: the NEVR in trigger scripts should all be the version in # which the package switched to systemd unit files and the comparision # should be less than. Using <= the last version with the sysV script won't # work for several reasons: # 1) disttag is different between Fedora releases # 2) An update in an old Fedora release may create a newer NEVR # Note that this means an update in an older Fedora release must be NEVR # lower than this. Freezing the version and release of the old package and # using a number after the disttag is one way to do this. Example: # httpd-1.0-1%{?dist} => httpd-1.0-1%{?dist}.1 %triggerun -- httpd < 1.0-2 # Save the current service runlevel info # User must manually run systemd-sysv-convert --apply httpd # to migrate them to systemd targets /usr/bin/systemd-sysv-convert --save httpd # If the package is allowed to autostart: /bin/systemctl enable apache-httpd.service >/dev/null 2>&1 # Run these because the SysV package being removed won't do them /sbin/chkconfig --del httpd >/dev/null 2>&1 || : /bin/systemctl try-restart apache-httpd.service >/dev/null 2>&1 || : /bin/systemctl daemon-reload >/dev/null 2>&1 || :