Home Wiki > openSUSE:Packaging scriptlet snippets
Sign up | Login

openSUSE:Packaging scriptlet snippets

tagline: From openSUSE


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.

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 particular interpreter instead of the default -p /bin/sh. If you use bashisms in a scriptlet, it would be prudent to specify -p /bin/bash.

The -p option may also be used to invoke one program directly without the shell indirection, e.g. by using %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 set to 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.) It is therefore 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 one way to ignore the exit status (by way of executing :, a shell syntactic shortcut doing the same as /bin/true) for those commands whether they succeeded 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 run before and after a package is installed, respectively. The scriptlets %preun and %postun are run before and after a package is uninstalled, respectively. The scriptlets %pretrans and %posttrans are run at start and end of a transaction, respectively. 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 afterwards to update the dynamic linker's cache files. These can be invoked like:

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

It is also common to declare these sections with the -p option (see above) as they are often the only program invoked in a scriptlet, and thus the implicit starting of a shell would be redundant. Prefer writing it like this:

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

Or, if you are working with a sub-package,

%post -n <sub-package> -p /sbin/ldconfig
%postun -n <sub-package> -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).

Have in mind that sections in a RPM spec file end where the next section begins. This can, in some cases, lead to unexpected errors. For example, if you have comments:

# The post section:
%post -p /sbin/ldconfig

# The postun section:
%postun -p /sbin/ldconfig

In this case, the "# The postun section:" comment, for RPM, actually belongs to the %post section (and the "# The post section:" comment actually belongs to the section before %post). In some cases, e.g. in the build system for SLE11 and SLE11_SP1, such a comment results in a situtation where an unintended argument '0' or '1' is added to the /sbin/ldconfig call which let it fail with errors like "/sbin/ldconfig: relative path '1' used to build cache". Therefore, traditional shell scriptlets for post/postun with an explicit "exit 0" line at the end are more fail safe — provided such errors should be ignored during RPM package install/uninstall. Otherwise, an explicit `exit 1` in those scriplets can be of use to enforce package install/upgrade/erase failure (see above). For example:

%post
/sbin/ldconfig || exit 1
do_what_is_only_nice_to_have
exit 0

%postun
/sbin/ldconfig
[ "$1" -eq 0 ] && undo_what_was_only_nice_to_have
exit 0

Users and groups

These are discussed on a separate page

Services

Initscripts Conventions

Full guidelines for SysV-style initscripts can be found here: openSUSE:Packaging_init_scripts

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.schema 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="%buildroot"
...

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 will not do anything and you will need to use the environment variable instead.

Here is the second part:

Requires(pre):       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 to first get the information on 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 that 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 we not 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. However, this has to happen before the new version is installed (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.

It is a good practice to avoid globbing in the file list and explicitly list the .schemas files there. This helps to prevent problems after an update. Both the file list and the %post script must be updated if a new .schemas file appears and rpm warns if not all installed files are mentioned in the file list. The file list from the example above should look like:

%files
[...]
%{_sysconfdir}/gconf/schemas/epiphany.schemas
%{_sysconfdir}/gconf/schemas/epiphany-lockdown.schemas

instead of:

%{_sysconfdir}/gconf/schemas/*.schemas

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
%install_info --info-dir=%{_infodir} %{_infodir}/%{name}.info.gz

%postun
%install_info_delete --info-dir=%{_infodir} %{_infodir}/%{name}.info.gz

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.

Scrollkeeper

Some distributions use the scrollkeeper cataloging system to keep track of documentation installed on the system. There is no need for doing this in openSUSE and no scrollkeeper-related macros need to be called in scriptlets.

MIME databases

Shared MIME info is a standard defined by freedesktop.org.

This feature is used when the package installs any file to %{_datadir}/mime and the distribution contains the shared-mime-info package.

Some packages call update-mime-database during installation with DESTDIR defined. It is a bug that can cause packaging of the actual MIME database instead of its component even if DESTDIR is set. The simplest work-around is to remove the generated files at the end of the %install section. Generally, everything except packages/*.xml are generated files and need to be removed.

Use this when a package drops an XML file in %{_datadir}/mime/packages up to 11.3:

Requires(post):    shared-mime-info
Requires(postun):  shared-mime-info

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

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

Or this starting with 11.4:

Requires(post):    shared-mime-info
Requires(postun):  shared-mime-info

%post
%mime_database_post

%postun
%mime_database_postun

MIME types can be tested with the package nautilus. It is enough to install the tested package, start or restart Nautilus, and look at properties of a corresponding file. The installed MIME type should be defined properly there.

The other database which needs to be updated when packages install desktop files defining MIME handlers needs the following macro in the scriptlets (starting from 11.4):

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

%post
%desktop_database_post

%postun
%desktop_database_postun

GTK+ icon cache

Some distributions (e.g. Fedora) call gtk-update-icon-cache in their %post/%postun scriptlets, when an application installs icons into one of the subdirectories in %{_datadir}/icons/ (such as hicolor).

In openSUSE, SuSEconfig used to be called after the installation of the packages, so nothing needed to be added to package .spec file. As SuSEconfig is only executed from YaST and not zypper or other install tools it is recommended to use the following macros (starting from 11.4):

%post
%icon_theme_cache_post

%postun
%icon_theme_cache_postun