Working with package dependencies on Red Hat Linux

Objective

Our goal is to get used to the tools available to find out information about package dependencies on an RPM based system.

Operating System and Software Versions

  • Operating system: Red Hat Enterprise Linux 7.5
  • Software: rpm 4.11, yum 3.4.3

Requirements

Privileged access to the system.

Difficulty

EASY

Conventions

  • # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command
  • $ – given linux commands to be executed as a regular non-privileged user

Introduction

RPM, which stands for Red Hat Package Manager, is a well-known and mature package manager used by all Red Hat flavor distributions, as well as SuSE. With RPM the packager can define relations between packages, and even with versions of packages – for example, an Apache Tomcat server needs proper Java environment present to be able to run.

On the other hand, to install a Java environment, you don’t need a Tomcat server – you may decide to run some different Java based application, maybe one written by yourself started by hand when needed to do it’s job. In other words, the Tomcat server depends on Java.

RPM can make a sysadmin’s life a lot easier by presenting these dependencies – and tools relying on RPM such as the rpm utility, or yum can automatically solve these dependencies, and install all additional packages needed for a new component to run properly.



Gathering information

To find out the list of packages that foo.bar package depends on, simply run:

# yum deplist foo.bar

And to find the list of packages that require (depend on) package foo.bar:

rpm -q --whatrequires foo.bar

A real life example with a generic package: bash. Let’s see what packages needed by the bash package:

# yum deplist bash

package: bash.x86_64 4.2.46-30.el7
  dependency: libc.so.6()(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.11)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.14)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.15)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.2.5)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.3)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.3.4)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.4)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libc.so.6(GLIBC_2.8)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libdl.so.2()(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libdl.so.2(GLIBC_2.2.5)(64bit)
   provider: glibc.x86_64 2.17-222.el7
  dependency: libtinfo.so.5()(64bit)
   provider: ncurses-libs.x86_64 5.9-14.20130511.el7_4
  dependency: rtld(GNU_HASH)
   provider: glibc.x86_64 2.17-222.el7
   provider: glibc.i686 2.17-222.el7

From the package perspective, bash is a very generic one, and as seen above, depends on a few core package. But if we’d like to install something much more dependent, say, the konzole KDE terminal emulator on a Red Hat Linux with a Gnome desktop manager, we may get more than one page long dependency list. And with konzole, the case is even more complicated, as it relies on QT and KDE packages, so in order to install it, you will need to install the whole KDE environment beside Gnome (what you can certainly do) to provide everything konzole needs.

To get a more insight of what packages will be installed, check the list provided by yum before starting the installation:

# yum install konsole

Resolving Dependencies
--> Running transaction check
---> Package konsole.x86_64 0:4.10.5-4.el7 will be installed
--> Processing Dependency: konsole-part = [...]


In the case of a Red Hat system with Gnome, it may take quite some time to resolve a KDE application’s dependencies for the first time, and when that is finished, yum will present the one sole package we asked for, with a nice small size. Followed by more than hundred packages installed for dependencies:

[...]
--> Running transaction check
---> Package boost-system.x86_64 0:1.53.0-27.el7 will be installed
---> Package boost-thread.x86_64 0:1.53.0-27.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

==============================================================================================================================
 Package                                Arch             Version                           Repository                    Size
==============================================================================================================================
Installing:
 konsole                                x86_64           4.10.5-4.el7                      rhel-7-server-rpms            78 k
Installing for dependencies:
 OpenEXR-libs
[...]

And in the summary we can see that the installation will use a lot more space on disk in the end, then the size of the package we need:

[...]
Transaction Summary
==============================================================================================================================
Install  1 Package (+120 Dependent packages)

Total download size: 108 M
Installed size: 307 M

This is a lot, but we got a useful information on how much space will be used. This is especially useful if we install many packages in one transaction.

While in this case the transaction is wasteful, the goal of dependencies is ultimately about saving resources: if someone implements some functionality in his/her code, and that can be called on the system, the next developer may not need to implement the same functionality again, but use the already existing implementation. For the konzole example, if you want to install akregator next time, the system will have many dependencies solved already, as kdepim package containing akregator also relies on qt, kdelibs, and such.

We can use rpm utility the get the information the other way around: let’s list the installed packages that require the bash package:

# rpm -q --whatrequires bash
dracut-033-535.el7.x86_64
initscripts-9.49.41-1.el7.x86_64
autofs-5.0.7-83.el7.x86_64
lvm2-2.02.177-4.el7.x86_64
rsyslog-8.24.0-16.el7.x86_64

Cleaning unneeded packages

If we keep our systems up to date, and change or extend their roles, “junk” packages will inevitably appear. In the package sense junk means no longer needed and/or deprecated packages. To follow the above example, we no longer need akregator, because we moved the “service” of RSS handling to a hypothetical central RSS concentrator within our system, so after migrating our feeds to the central place, we uninstall the local RSS handling application. That will not remove all the KDE packages, as many other packages may depend on them. But if not, those packages are junk, and will consume resources, including longer update times, as yum by default will update everything blindly it finds new packages/errata for.

Spending resources on upgrading a few unneeded packages on a laptop with broadband connection and SSD may not seem to be an issue, but imagine a datacenter with hundreds or thousands of computers, and you get the picture. It is generally a good idea to keep all systems simple, and resource management is only one point. The more complex a system, the more error prone it is. More components mean more possible bugs.

To get an overview on unneeded packages installed on the system, we can use yum and package-cleanup the same way as on CentOS, or another feature of yum, autoremove:

yum autoremove


The packages these tools mark as unneeded are not identical.

When using any of these tools it is advised to double-check what yum is going to remove, and possibly test what the cleaning will result in on testing machines with identical package content before cleaning up the production systems.

These tools are indeed clever, but not all-knowing: for example, there will be no entry in the rpm database about a custom PHP application running on top of a webserver that calls cups to print out incoming orders on a printer connected to the server. That is, there can be an entry if the application is packaged with the right dependencies included, and installed properly with rpm or yum – but that takes effort, and all services need to be packaged the same way if you want to feel safe with yum-based automatic cleanups.

Solving dependency problems

Especially in large environments, there can be dependency issues while installing or upgrading systems.

The below screenshot shows a simple issue:

Solving dependencies with rpm

Solving dependencies with rpm

In the above terminal screen we try to install the nrpe package, the client needed to monitor many aspects of the system with Nagios. We downloaded the client for the distribution, but both rpm and yum fails with the same error: the nrpe package requires (depends on) the nagios-common package. In this example we can obtain the needed package from the same source, and when installing both of them the rpm utility sees that the dependency we failed on earlier will be satisfied by the end of the transaction and installs both packages, exiting silently with success.

Conclusion

Yum and rpm are essential tools when working with distributions using the RPM package manager. By knowing the toolset it is much easier, and usually safer to solve install, upgrade and modify tasks on the software environment of a given system.