Ever wonder what makes up an RPM
package? I sure have. Ever since I was a boy, I’ve lain awake at night and wondered about the contents of the package. Does anyone really know?
In particular, we’ll want to know a package’s dependencies and files and look into the what makes up an RPM
package and how we can inspect it without first installing it.
The commands in this article were run on a
CentOS
7 distribution using the Yellowdog Updater, Modified package manager.
You may be interested in learning about deb
packages in On Inspecting deb Packages.
- Downloading the RPM Package
- Listing the Package Dependencies
- Listing the Package Files
- So, What Is An RPM Package?
- The
SPEC
File - Summary
- References
Downloading the RPM Package
The first thing we’ll want to do is download the RPM
package without installing it.
There are two kinds of packages, source RPM packages (
SRPM
) and binaryRPM
packages, both built from the sameSPEC
file (more on that later).
yumdownloader
The main tool we’ll use to accomplish this is yumdownloader
. This utility will only download, so it is perfect for our use case. Let’s take a look at its options:
Option | Description |
---|---|
--archlist |
Limit the query to packages of given and compatible architectures. |
--destdir |
Specify a destination directory for the download. Defaults to the current directory. |
--resolve |
When downloading RPM s, resolve dependencies and also download the required packages. |
--source |
Instead of downloading the binary RPMs, download the source RPMs. |
--urls |
Instead of downloading RPM s, list the URL s that would be downloaded. |
yumdownloader
will inherit all other options from yum
.
For the examples in this article, we’ll use our good friend tmux
. Before we download this little fella, let’s see where he lives:
$ yumdownloader --quiet --urls tmux
http://volico.mm.fcix.net/centos/7.9.2009/os/x86_64/Packages/tmux-1.8-4.el7.x86_64.rpm
Now, let’s do the actual download:
$ yumdownloader --quiet tmux
yum
can also download without installing:$ sudo yum install --downloadonly --downloaddir . tmux
If tmux
had any runtime dependencies, we could also download them (along with tmux
itself) by specifying the --resolve
option:
$ yumdownloader -q --resolve tmux
For good measure, we’ll also download the SRPM
, because we’ll need it later:
$ yumdownloader -q --source tmux
Now that we have that hard work out of the way, we can begin our inspection, my dear Watson.
Listing the Package Dependencies
None of the following tools need to have first had the package downloaded before listing its dependencies. Depending on the utility, it either expects a package name or a
URL
.
Ok, listing the package dependencies is easy enough. We’ll use yum
for this, because the yumdownloader
utility with the deplist
subcommand will download all of the dependencies - if there are any dependencies to download - which is not what we want (remember, yumdownloader
inherits yum
s options).
yum
In the case of tmux
, there are no external dependencies. Regardless, we’ll still use yum
:
$ yum --quiet deplist tmux
package: tmux.x86_64 1.8-4.el7
dependency: /bin/sh
provider: bash.x86_64 4.2.46-35.el7_9
dependency: libc.so.6(GLIBC_2.14)(64bit)
provider: glibc.x86_64 2.17-326.el7_9
dependency: libevent-2.0.so.5()(64bit)
provider: libevent.x86_64 2.0.21-4.el7
dependency: libncurses.so.5()(64bit) provider: ncurses-libs.x86_64 5.9-14.20130511.el7_4
dependency: libresolv.so.2()(64bit)
provider: glibc.x86_64 2.17-326.el7_9
dependency: libresolv.so.2(GLIBC_2.2.5)(64bit)
provider: glibc.x86_64 2.17-326.el7_9
dependency: libtinfo.so.5()(64bit)
provider: ncurses-libs.x86_64 5.9-14.20130511.el7_4
dependency: libutil.so.1()(64bit)
provider: glibc.x86_64 2.17-326.el7_9
dependency: libutil.so.1(GLIBC_2.2.5)(64bit)
provider: glibc.x86_64 2.17-326.el7_9
dependency: rtld(GNU_HASH)
provider: glibc.x86_64 2.17-326.el7_9 provider: glibc.i686 2.17-326.el7_9
Note that this took the package’s name, not a path to the file on the system.
rpm
You can also use the rpm
utility to get the same information using the --requires
switch. The first example queries against the downloaded file (hence, the relative path to the file), and the second example will use a URL
.
Querying a local RPM
package:
$ rpm -qp --requires tmux-1.8-4.el7.x86_64.rpm
/bin/sh
/bin/sh
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_2.8)(64bit)
libevent-2.0.so.5()(64bit)
libncurses.so.5()(64bit)
libresolv.so.2()(64bit)
libresolv.so.2(GLIBC_2.2.5)(64bit)
libtinfo.so.5()(64bit)
libutil.so.1()(64bit)
libutil.so.1(GLIBC_2.2.5)(64bit)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rtld(GNU_HASH)
rpmlib(PayloadIsXz) <= 5.2-1
Querying a URL
(this is especially useful when you don’t know the download link):
$ rpm -pq --requires $(yumdownloader --quiet --urls tmux)
/bin/sh
/bin/sh
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_2.8)(64bit)
libevent-2.0.so.5()(64bit)
libncurses.so.5()(64bit)
libresolv.so.2()(64bit)
libresolv.so.2(GLIBC_2.2.5)(64bit)
libtinfo.so.5()(64bit)
libutil.so.1()(64bit)
libutil.so.1(GLIBC_2.2.5)(64bit)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rtld(GNU_HASH)
rpmlib(PayloadIsXz) <= 5.2-1
repoquery
If you would like to understand what are a package’s dependencies without downloading it, use the repoquery
utility (part of the yum-utils
package).
You can query package information from any of your listed package repositories, which will download the yum
repository metadata of the package and update the cache (you can point the tool to the local cache instead of a remote repository, even).
This can be extremely handy when needing to view the contents of a package not installed and downloaded on your local system.
It will only provide information about packages that are in the configured repositories on the system.
Here’s an example. We’ll first try to get the information from the cache instead of going out on the Internet:
$ repoquery --list --cache memcached
Caching enabled but no local cache of /var/tmp/yum-vagrant-iCF5ZP/x86_64/7/base/d6d94c7d406fe7ad4902a97104b39a0d8299451832a97f31d71653ba982c955b-filelists.sqlite.bz2 from base/7/x86_64
Ok, looks like the cache hasn’t been built because nothing has been downloaded yet. Let’s remedy that by running the same query without the --cache
switch:
$ repoquery --list memcached
/etc/sysconfig/memcached
/usr/bin/memcached
/usr/bin/memcached-tool
/usr/lib/systemd/system/memcached.service
/usr/share/doc/memcached-1.4.15
/usr/share/doc/memcached-1.4.15/AUTHORS
/usr/share/doc/memcached-1.4.15/CONTRIBUTORS
/usr/share/doc/memcached-1.4.15/COPYING
/usr/share/doc/memcached-1.4.15/ChangeLog
/usr/share/doc/memcached-1.4.15/NEWS
/usr/share/doc/memcached-1.4.15/README.md
/usr/share/doc/memcached-1.4.15/protocol.txt
/usr/share/doc/memcached-1.4.15/readme.txt
/usr/share/doc/memcached-1.4.15/threads.txt
/usr/share/man/man1/memcached-tool.1.gz
/usr/share/man/man1/memcached.1.gz
If we were now to run the same query against the cache, we’d get the same information as above.
If you’re wondering, the
yum
cache is located in/var/cache/yum/$basearch/$releasever/
, where$basearch
and$releasever
areyum
variables referring to the system architecture and the OS release version, respectively.For instance, I’ve been running these examples on an older CentOS 7 distribution on a machine with an Intel processor, and its
yum
cache is located at/var/cache/yum/x86_64/7/
.
If you’re curious about the list of configured package repositories are on the system (and from where the dependencies will be downloaded), run the following command:
$ yum -q repolist
Or:
$ yum repoinfo
The second command is the same as
yum -v repolist
.
Ok, onwards and upwards.
Listing the Package Files
As we just saw with listing the package dependencies, we can use more than one tool to accomplish this and the file doesn’t need to have been downloaded.
rpm
Querying a local RPM
package:
$ rpm -qlp tmux-1.8-4.el7.x86_64.rpm
/usr/bin/tmux
/usr/share/doc/tmux-1.8
/usr/share/doc/tmux-1.8/CHANGES
/usr/share/doc/tmux-1.8/FAQ
/usr/share/doc/tmux-1.8/TODO
/usr/share/doc/tmux-1.8/examples
/usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
/usr/share/doc/tmux-1.8/examples/h-boetes.conf
/usr/share/doc/tmux-1.8/examples/n-marriott.conf
/usr/share/doc/tmux-1.8/examples/screen-keys.conf
/usr/share/doc/tmux-1.8/examples/t-williams.conf
/usr/share/doc/tmux-1.8/examples/tmux.vim
/usr/share/doc/tmux-1.8/examples/tmux_backup.sh
/usr/share/doc/tmux-1.8/examples/vim-keys.conf
/usr/share/man/man1/tmux.1.gz
Option | Description |
---|---|
-q , --query |
Query a package |
-l , --list |
List files in a package |
-p , --package |
Query an uninstalled package |
Here, we’ll get the list of files again, but this time we’ll also get the long listing of each file:
$ rpm -qlpv tmux-1.8-4.el7.x86_64.rpm
-rwxr-xr-x 1 root root 423128 Jun 10 2014 /usr/bin/tmux
drwxr-xr-x 2 root root 0 Jun 10 2014 /usr/share/doc/tmux-1.8
-rw-r--r-- 1 root root 78119 Mar 26 2013 /usr/share/doc/tmux-1.8/CHANGES
-rw-r--r-- 1 root root 16972 Feb 24 2013 /usr/share/doc/tmux-1.8/FAQ
-rw-r--r-- 1 root root 7928 Feb 24 2013 /usr/share/doc/tmux-1.8/TODO
drwxr-xr-x 2 root root 0 Jun 10 2014 /usr/share/doc/tmux-1.8/examples
-rw-r--r-- 1 root root 2014 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
-rw-r--r-- 1 root root 913 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/h-boetes.conf
-rw-r--r-- 1 root root 2338 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/n-marriott.conf
-rw-r--r-- 1 root root 1805 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/screen-keys.conf
-rw-r--r-- 1 root root 2789 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/t-williams.conf
-rw-r--r-- 1 root root 5385 Feb 24 2013 /usr/share/doc/tmux-1.8/examples/tmux.vim
-rw-r--r-- 1 root root 2513 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/tmux_backup.sh
-rw-r--r-- 1 root root 1088 Feb 10 2013 /usr/share/doc/tmux-1.8/examples/vim-keys.conf
-rw-r--r-- 1 root root 26855 Jun 10 2014 /usr/share/man/man1/tmux.1.gz
Option | Description |
---|---|
-q , --query |
Query a package |
-l , --list |
List files in a package |
-p , --package |
Query an uninstalled package |
-v , --verbose |
Print verbose information, such as the file permissions and ownership (like long listing, ls -l ) |
Querying a URL
(this is especially useful when you don’t know the download link):
$ rpm -qlp $(yumdownloader --quiet --urls tmux)
/usr/bin/tmux
/usr/share/doc/tmux-1.8
/usr/share/doc/tmux-1.8/CHANGES
/usr/share/doc/tmux-1.8/FAQ
/usr/share/doc/tmux-1.8/TODO
/usr/share/doc/tmux-1.8/examples
/usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
/usr/share/doc/tmux-1.8/examples/h-boetes.conf
/usr/share/doc/tmux-1.8/examples/n-marriott.conf
/usr/share/doc/tmux-1.8/examples/screen-keys.conf
/usr/share/doc/tmux-1.8/examples/t-williams.conf
/usr/share/doc/tmux-1.8/examples/tmux.vim
/usr/share/doc/tmux-1.8/examples/tmux_backup.sh
/usr/share/doc/tmux-1.8/examples/vim-keys.conf
/usr/share/man/man1/tmux.1.gz
repoquery
To get a list of files using the repoquery
utility, use the -l
or --list
option:
$ repoquery --list tmux
/usr/bin/tmux
/usr/share/doc/tmux-1.8
/usr/share/doc/tmux-1.8/CHANGES
/usr/share/doc/tmux-1.8/FAQ
/usr/share/doc/tmux-1.8/TODO
/usr/share/doc/tmux-1.8/examples
/usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
/usr/share/doc/tmux-1.8/examples/h-boetes.conf
/usr/share/doc/tmux-1.8/examples/n-marriott.conf
/usr/share/doc/tmux-1.8/examples/screen-keys.conf
/usr/share/doc/tmux-1.8/examples/t-williams.conf
/usr/share/doc/tmux-1.8/examples/tmux.vim
/usr/share/doc/tmux-1.8/examples/tmux_backup.sh
/usr/share/doc/tmux-1.8/examples/vim-keys.conf
/usr/share/man/man1/tmux.1.gz
Like taking candy from a baby.
So, What Is An RPM Package?
An RPM
package consists simply a header on top of a cpio
archive.
The former is metadata and includes information such as the package name, version, file list and architecture, et al., while the latter is an archive of the files (binary and text, including man pages) that will be installed on the local filesystem.
The cpio
archive is essentially a chroot
] (the paths and directory hierarchies created in the BUILDROOT
directory when creating the package), and it mirrors the locations of the files to be installed. These will be locations such as /usr/sbin
, /usr/bin/
and /usr/share/doc/
, et al., and will contain binaries, man pages and other files which were all created by the directives in the SPEC
file and its different sections such as %build
, %files
and %install
, etc.
The
SPEC
file, incidentally, is known as the “recipe” for how the package is created.
cpio
archive
The archive is a chroot
that is created by the package in the %install
section of the SPEC
file (as we’ll see below). These package assets are created in the BUILDROOT
, the directory under which rpm
will look for any files to package. It is the same directory hierarchy that should be used when the files are installed on the destination filesystem.
We’ll use the rpm2cpio
utility to extract the cpio
archive from an RPM
package. Here, we’ll extract the cpio
archive from the RPM
package, piping the results to cpio
which creates the files on the local filesystem:
$ rpm2cpio tmux-1.8-4.el7.x86_64.rpm | cpio -idmv
./usr/bin/tmux
./usr/share/doc/tmux-1.8
./usr/share/doc/tmux-1.8/CHANGES
./usr/share/doc/tmux-1.8/FAQ
./usr/share/doc/tmux-1.8/TODO
./usr/share/doc/tmux-1.8/examples
./usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
./usr/share/doc/tmux-1.8/examples/h-boetes.conf
./usr/share/doc/tmux-1.8/examples/n-marriott.conf
./usr/share/doc/tmux-1.8/examples/screen-keys.conf
./usr/share/doc/tmux-1.8/examples/t-williams.conf
./usr/share/doc/tmux-1.8/examples/tmux.vim
./usr/share/doc/tmux-1.8/examples/tmux_backup.sh
./usr/share/doc/tmux-1.8/examples/vim-keys.conf
./usr/share/man/man1/tmux.1.gz
1122 blocks
Coupling the -t
switch with -v
will also produce a long listing of the processed files:
$ rpm2cpio tmux-1.8-4.el7.x86_64.rpm | cpio -idmtv
-rwxr-xr-x 1 root root 423128 Jun 10 2014 ./usr/bin/tmux
drwxr-xr-x 3 root root 0 Jun 10 2014 ./usr/share/doc/tmux-1.8
-rw-r--r-- 1 root root 78119 Mar 26 2013 ./usr/share/doc/tmux-1.8/CHANGES
-rw-r--r-- 1 root root 16972 Feb 24 2013 ./usr/share/doc/tmux-1.8/FAQ
-rw-r--r-- 1 root root 7928 Feb 24 2013 ./usr/share/doc/tmux-1.8/TODO
drwxr-xr-x 2 root root 0 Jun 10 2014 ./usr/share/doc/tmux-1.8/examples
-rw-r--r-- 1 root root 2014 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
-rw-r--r-- 1 root root 913 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/h-boetes.conf
-rw-r--r-- 1 root root 2338 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/n-marriott.conf
-rw-r--r-- 1 root root 1805 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/screen-keys.conf
-rw-r--r-- 1 root root 2789 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/t-williams.conf
-rw-r--r-- 1 root root 5385 Feb 24 2013 ./usr/share/doc/tmux-1.8/examples/tmux.vim
-rw-r--r-- 1 root root 2513 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/tmux_backup.sh
-rw-r--r-- 1 root root 1088 Feb 10 2013 ./usr/share/doc/tmux-1.8/examples/vim-keys.conf
-rw-r--r-- 1 root root 26855 Jun 10 2014 ./usr/share/man/man1/tmux.1.gz
1122 blocks
Option | Description |
---|---|
-i , --extract |
Run in copy-in mode |
-d , --make-directories |
Create leading directories where needed |
-m , --preserve-modification-time |
Retain previous file modification times when creating files |
-t , --list |
Print a table of contents of the input |
-v , --verbose |
List the files processed, or with -t', give an ls -l' style table of contents listing |
Both of the previous examples will create the following directory tree:
$ tree usr/
usr/
├── bin
│ └── tmux
└── share
├── doc
│ └── tmux-1.8
│ ├── CHANGES
│ ├── examples
│ │ ├── bash_completion_tmux.sh
│ │ ├── h-boetes.conf
│ │ ├── n-marriott.conf
│ │ ├── screen-keys.conf
│ │ ├── tmux_backup.sh
│ │ ├── tmux.vim
│ │ ├── t-williams.conf
│ │ └── vim-keys.conf
│ ├── FAQ
│ └── TODO
└── man
└── man1
└── tmux.1.gz
7 directories, 13 files
Of course, the prior examples were both extracted the cpio
archive for a binary RPM
package. But what about a source RPM
? Let’s give it a whirl:
$ rpm2cpio tmux-1.8-4.el7.src.rpm | cpio -idmv
tmux-1.8.tar.gz
tmux.spec
826 blocks
This extracted the SPEC
file and any sources. Although not shown here because the package doesn’t contain any, it also would have extracted any patches that would have been part of the package.
This is important to keep in mind when you want to access the SPEC
file that the package builder used.
BUILDROOT
To understand what the BUILDROOT
is, I’m just going to quote directly from the section on BuildRoots from the RPM Packaging Guide, as I couldn’t write it any better:
In the context of RPM packaging, “buildroot” is a chroot
environment. This means that the build artifacts are placed here using the same filesystem hierarchy as will be in the end user’s system, with “buildroot” acting as the root directory. The placement of build artifacts should comply with the filesystem hierarchy standard of the end user’s system.
The files in “buildroot” are later put into a cpio
archive, which becomes the main part of the RPM
. When RPM
is installed on the end user’s system, these files are extracted in the root directory, preserving the correct hierarchy.
Header
All of the information a package contains, apart from signatures and the actual files, is in a part of the package called the header. So, the header contains metadata about the package, and the RPM
package manager uses this metadata to determine dependencies, where to install files, and other information.
This information is able to be queried. Each piece of information in the header has a tag associated with it, and we can print these tags with formatting information.
RPM
is aware of a great number of tags, and we can see what they are by running the following command:
$ rpm --querytags
[snipped output]
$ rpm --querytags | wc -l
205
On my CentOS
distribution, RPM
is aware of 205 tags. Holy Zap!
For example, we can get a list of the name and size of every installed package on the system. For this, we’ll use the --queryformat
option:
$ rpm -qa --queryformat "%{NAME} %{SIZE}\n"
To get the name and size of the uninstalled tmux
package, issue the following command with the -p
flag:
$ rpm -qp --queryformat "%{NAME} %{SIZE}\n" tmux-1.8-4.el7.x86_64.rpm
tmux 571847
Here, we are getting a list of all the files in the uninstalled tmux
package. Note the addition of additional formatting information, where it’s printing the FILENAMES
in the first 60 bytes and then right-aligning the FILESIZES
.
Further, the aformentioned tags are arrays, and it’s iterating through them (similar to bash
variables in this respect):
$ rpm -qp --queryformat "[%-60{FILENAMES} %10{FILESIZES}\n]" tmux-1.8-4.el7.x86_64.rpm
/usr/bin/tmux 423128
/usr/share/doc/tmux-1.8 4096
/usr/share/doc/tmux-1.8/CHANGES 78119
/usr/share/doc/tmux-1.8/FAQ 16972
/usr/share/doc/tmux-1.8/TODO 7928
/usr/share/doc/tmux-1.8/examples 4096
/usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh 2014
/usr/share/doc/tmux-1.8/examples/h-boetes.conf 913
/usr/share/doc/tmux-1.8/examples/n-marriott.conf 2338
/usr/share/doc/tmux-1.8/examples/screen-keys.conf 1805
/usr/share/doc/tmux-1.8/examples/t-williams.conf 2789
/usr/share/doc/tmux-1.8/examples/tmux.vim 5385
/usr/share/doc/tmux-1.8/examples/tmux_backup.sh 2513
/usr/share/doc/tmux-1.8/examples/vim-keys.conf 1088
/usr/share/man/man1/tmux.1.gz 26855
Let’s get a long listing of the files:
$ rpm -qp --queryformat "[%{FILEMODES:perms} %{FILENAMES}\n]" tmux-1.8-4.el7.x86_64.rpm
-rwxr-xr-x /usr/bin/tmux
drwxr-xr-x /usr/share/doc/tmux-1.8
-rw-r--r-- /usr/share/doc/tmux-1.8/CHANGES
-rw-r--r-- /usr/share/doc/tmux-1.8/FAQ
-rw-r--r-- /usr/share/doc/tmux-1.8/TODO
drwxr-xr-x /usr/share/doc/tmux-1.8/examples
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/bash_completion_tmux.sh
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/h-boetes.conf
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/n-marriott.conf
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/screen-keys.conf
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/t-williams.conf
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/tmux.vim
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/tmux_backup.sh
-rw-r--r-- /usr/share/doc/tmux-1.8/examples/vim-keys.conf
-rw-r--r-- /usr/share/man/man1/tmux.1.gz
Let’s turn now to viewing scriptlets. If you’re unsure what the tag names are, simply pipe the full list of tag names to grep
. We know that if there are one or more scriptlets, they’ll likely to be post-install and post-uninstall. Let’s try it out:
$ rpm --querytags | grep -i post
POSTIN
POSTINFLAGS
POSTINPROG
POSTTRANS
POSTTRANSFLAGS
POSTTRANSPROG
POSTUN
POSTUNFLAGS
POSTUNPROG
$ rpm -qp --queryformat "%{POSTIN}\n%{POSTUN}\n" tmux-1.8-4.el7.x86_64.rpm
if [ ! -f /etc/shells ] ; then
echo "/usr/bin/tmux" > /etc/shells
else
grep -q "^/usr/bin/tmux$" /etc/shells || echo "/usr/bin/tmux" >> /etc/shells
fi
if [ $1 -eq 0 ] && [ -f /etc/shells ]; then
sed -i '\!^/usr/bin/tmux$!d' /etc/shells
fi
There they are, those little rascals!
A scriptlet is part of the package and runs during specific events, like pre- and post-installation or pre- and post-uninstallation of a package.
Here’s a random example:
$ rpm -qp --queryformat "%{URL} %{PACKAGER}\n" tmux-1.8-4.el7.x86_64.rpm
http://sourceforge.net/projects/tmux CentOS BuildSystem <http://bugs.centos.org>
Let’s now take a look at the SPEC
file.
The SPEC
File
First, we need to access it. How do we do that?
The SPEC
file is the recipe on how to create an RPM
package. However, it’s (usually) not packaged in a binary RPM
, so the easiest way to obtain it is to download the SRPM
(the source RPM
package).
We’ve already down that, but quickly, while he’s away, we’ll list the steps.
$ yumdownloader -q --source tmux
Once we have the SRPM
, we’re home free. In the last section, we got our dirty little hands on the SPEC
file by extracting the acpio
archive with the rpm2cpio
utility and then piped that to cpio
to create the files on disk.
However, we can install it using rpm
, and this will create the rpmbuild
directory (for those familiar with the rpmdev-setuptree
tool, this will look familiar):
$ rpm -i tmux-1.8-4.el7.src.rpm
$ tree rpmbuild/
rpmbuild/
├── SOURCES
│ └── tmux-1.8.tar.gz
└── SPECS
└── tmux.spec
2 directories, 2 files
There it is. Let’s look at its contents:
Name: tmux
Version: 1.8
Release: 4%{?dist}
Summary: A terminal multiplexer
Group: Applications/System
# Most of the source is ISC licensed; some of the files in compat/ are 2 and
# 3 clause BSD licensed.
License: ISC and BSD
URL: http://sourceforge.net/projects/tmux
Source0: http://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz
BuildRequires: ncurses-devel
BuildRequires: libevent-devel
%description
tmux is a "terminal multiplexer." It enables a number of terminals (or
windows) to be accessed and controlled from a single terminal. tmux is
intended to be a simple, modern, BSD-licensed alternative to programs such
as GNU Screen.
%prep
%setup -q
%build
%configure
make %{?_smp_mflags} LDFLAGS="%{optflags}"
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot} INSTALLBIN="install -p -m 755" INSTALLMAN="install -p -m 644"
%post
if [ ! -f %{_sysconfdir}/shells ] ; then
echo "%{_bindir}/tmux" > %{_sysconfdir}/shells
else
grep -q "^%{_bindir}/tmux$" %{_sysconfdir}/shells || echo "%{_bindir}/tmux" >> %{_sysconfdir}/shellsfi
%postun
if [ $1 -eq 0 ] && [ -f %{_sysconfdir}/shells ]; then sed -i '\!^%{_bindir}/tmux$!d' %{_sysconfdir}/shells
fi
%files
%defattr(-,root,root,-)
%doc CHANGES FAQ TODO examples/
%{_bindir}/tmux
%{_mandir}/man1/tmux.1.*
%changelog
* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 1.8-4
- Mass rebuild 2014-01-24
* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 1.8-3
- Mass rebuild 2013-12-27
...
You can see the %post
(POSTIN
) and %postun
(POSTUN
) scriptlets that we queried earlier. Note there though, the macros haven’t been expanded. Compare the scriptlets above to the ones that we got earlier using the --query-format
switch to see the difference between the unexpanded and expanded macros.
For the curious, the macros were replaced by
rpmbuild
during the package creation process.
By the way, here’s another way to list its postinstall
and postuninstall
scripts using the rpm
utility and the --scripts
switch:
$ rpm -qp --scripts tmux-1.8-4.el7.x86_64.rpm
postinstall scriptlet (using /bin/sh):
if [ ! -f /etc/shells ] ; then
echo "/usr/bin/tmux" > /etc/shells
else
grep -q "^/usr/bin/tmux$" /etc/shells || echo "/usr/bin/tmux" >> /etc/shells
fi
postuninstall scriptlet (using /bin/sh):
if [ $1 -eq 0 ] && [ -f /etc/shells ]; then
sed -i '\!^/usr/bin/tmux$!d' /etc/shells
fi
So, what are the scriptlets doing?
Here, the scripts are simply adding the location of the
tmux
binary to the list of valid login shells in/etc/shells
or removing it, depending on the operation (/etc/shells
is itself consulted by utilities likechsh
, et al.).$ cat /etc/shells # /etc/shells: valid login shells /bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /bin/dash /usr/bin/dash /usr/bin/tmux /bin/mksh /usr/bin/mksh /bin/mksh-static /usr/lib/klibc/bin/mksh-static
This is quite common.
Option | Description |
---|---|
-q , --query |
Query a package |
-p , --package |
Query an uninstalled package |
--scripts |
List the package specific scriptlet(s) that are used as part of the installation and uninstallation processes |
To view the scriptlet(s) of an installed package, simply remove the -p
flag and pass the name of the package instead of the path to the RPM
:
$ rpm -q --scripts iputils
postinstall scriptlet (using /bin/sh):
if [ $1 -eq 1 ] ; then
# Initial installation
systemctl preset rdisc.service >/dev/null 2>&1 || :
fi
preuninstall scriptlet (using /bin/sh):
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
systemctl --no-reload disable rdisc.service > /dev/null 2>&1 || :
systemctl stop rdisc.service > /dev/null 2>&1 || :
fi
postuninstall scriptlet (using /bin/sh):
systemctl daemon-reload >/dev/null 2>&1 || :
if [ $1 -ge 1 ] ; then
# Package upgrade, not uninstall
systemctl try-restart rdisc.service >/dev/null 2>&1 || :
fi
To list all of the installed packages, do the following:
$ rpm -qa
Or, use
yum
:$ yum list installed
Bear in mind that the scriptlets are defined in the SPEC
file and are not included in a binary RPM
(unless the maintainer explicitly included it, and usually there’s no reason to do that).
Summary
As we’ve seen, there are many ways and several tools that can inspect a package before installation that all do similar things. It can be a bit confusing trying to determine the simplest way to do a particular task, and hopefully this guide gets you started down that path.