Maximum RPM

Taking the RPM Package Manager to the Limit

Edward C. Bailey

Red Hat, Inc.

%ghost description: Paul Nasrat

Start of updates, epoch, rpmbuild, etc: Matthias Saou

Various typo fixes, %check section, documentation on --recompil: Ville Skyttä

Copyright © 2000 by Red Hat, Inc. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/).

Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.

Revision History
Revision 1.0 17-February-1997
First edition (Produced/printed by Red Hat Software)
Revision 1.1 ??-???-1997
Revised for production by Sams Publishing
Revision 1.2 29-February-2000
Translated into DocBook
Revision 1.3 05-March-2003
Start of the update process

Table of Contents

Preface
Linux and RPM — A Brief History
Parts of the book, and who they're for
Acknowledgements
I. RPM and Computer Users — How to Use RPM to Effectively Manage Your Computer
1. An Introduction to Package Management
What are Packages, and Why Manage Them?
Enter the Package
Manage Your Packages, or They Will Manage You
Package Management: How to Do It?
Ancestors of RPM
RPM Design Goals
Make it easy to get packages on and off the system
Make it easy to verify a package was installed correctly
Make it easy for the package builder
Make it start with the original source code
Make it work on different computer architectures
What's in a package?
RPM's Package Labels
Labels And Names: Similar, But Distinct
Package-wide Information
Per-file Information
Let's Get Started
2. Using RPM to Install Packages
rpm -i — What does it do?
Performing dependency checks:
Checking for conflicts:
Performing any tasks required before the install:
Deciding what to do with config files:
Unpacking files from the package and putting them in the proper place:
Performing any tasks required after the install:
Keeping track of what it did:
Performing an Install
URLs — Another Way to Specify Package Files
A warning message you might never see
Two handy options
Getting a bit more feedback with -v
-h: Perfect for the Impatient
Additional options to rpm -i
Getting a lot more information with -vv
--test: Perform Installation Tests Only
--replacepkgs: Install the Package Even If Already Installed
--replacefiles: Install the Package Even If It Replaces Another Package's Files
--nodeps: Do Not Check Dependencies Before Installing Package
--force: The Big Hammer
--excludedocs: Do Not Install Documentation For This Package
--includedocs: Install Documentation For This Package
--prefix <path>: Relocate the package to <path>, if possible
--noscripts: Do Not Execute Pre- and Post-install Scripts
--percent: Not Meant for Human Consumption
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
--root <path>: Use <path> As An Alternate Root
--dbpath <path>: Use <path> To Find RPM Database
--ftpport <port>: Use <port> In FTP-based Installs
--ftpproxy <host>: Use <host> As Proxy In FTP-based Installs
--ignorearch: Do Not Verify Package Architecture
--ignoreos: Do Not Verify Package Operating System
3. Using RPM to Erase Packages
rpm -e — What Does it Do?
Erasing a Package
Getting More Information With -vv
Additional Options
--test — Go Through the Process of Erasing the Package, But Do Not Erase It
--nodeps: Do Not Check Dependencies Before Erasing Package
--noscripts — Do Not Execute Pre- and Post-uninstall Scripts
--rcfile <rcfile> — Read <rcfile> For RPM Defaults
--root <path> — Use <path> As the Root
--dbpath <path>: Use <path> To Find RPM Database
rpm -e and Config files
Watch Out!
4. Using RPM to Upgrade Packages
rpm -U — What Does it Do?
Config file magic
Upgrading a Package
rpm -U's Dirty Little Secret
They're Nearly Identical…
--oldpackage: Upgrade To An Older Version
--force: The Big Hammer
--noscripts: Do Not Execute Install and Uninstall Scripts
5. Getting Information About Packages
rpm -q — What does it do?
The Parts of an RPM Query
Query Commands, Part One: Package Selection
Query Commands, Part Two: Information Selection
Getting a lot more information with -vv
--root <path>: Use <path> As An Alternate Root
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
--dbpath <path>: Use <path> To Find RPM Database
A Few Handy Queries
Finding Config Files Based on a Program Name
Learning More About an Uninstalled Package
Finding Documentation for a Specific Package
Finding Similar Packages
Finding Recently Installed Packages, Part I
Finding Recently Installed Packages, Part II
Finding the Largest Installed Packages
6. Using RPM to Verify Installed Packages
rpm -V — What Does it Do?
What Does it Verify?
When Verification Fails — rpm -V Output
Other Verification Failure Messages
Selecting What to Verify, and How
The Package Label — Verify an Installed Package Against the RPM Database
-a — Verify All Installed Packages Against the RPM Database
-f <file> — Verify the Package Owning <file> Against the RPM Database
-p <file> — Verify Against a Specific Package File
-g <group> — Verify Packages Belonging To <group>
--nodeps: Do Not Check Dependencies During Verification
--noscripts: Do Not Execute Verification Script
--nofiles: Do Not Verify File Attributes
-v — Display Additional Information
-vv — Display Debugging Information
--dbpath <path>: Use <path> To Find RPM Database
--root <path>: Set Alternate Root to <path>
--rcfile <rcfile>: Set Alternate rpmrc file to <rcfile>
We've Lied to You…
RPM Controls What Gets Verified
7. Using RPM to Verify Package Files
rpm -K — What Does it Do?
Pretty Good Privacy: RPM's Assistant
Configuring PGP for rpm -K
Using rpm -K
-v — Display Additional Information
When the Package is Not Signed
When You Are Missing the Correct Public Key
When a Package Just Doesn't Verify
--nopgp — Do Not Verify Any PGP Signatures
-vv — Display Debugging Information
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
8. Miscellanea
Other RPM Options
--rebuilddb — Rebuild RPM database
--initdb — Create a New RPM Database
--quiet — Produce as little output as possible
--help — Display a help message
--version — Display the current RPM version
Using rpm2cpio
rpm2cpio — What does it do?
A more real-world example — Listing the files in a package file
Extracting one or more files from a package file
Source Package Files and How To Use Them
A gentle introduction to source code
Do you really need more information than this?
So what can I do with it?
Stick with us!
II. RPM and Developers — How to Distribute Your Software More Easily With RPM
9. The Philosophy Behind RPM
Pristine Sources
Easy Builds
Reproducible Builds
Unattended Builds
Multi-architecture/operating system Support
Easier For Your Users
Easy Upgrades
Intelligent Configuration File Handling
Powerful Query Capabilities
Easy Package Verification
To Summarize…
10. The Basics of Developing With RPM
The Inputs
The Sources
The Patches
The Spec File
The Engine: RPM
The Outputs
The Source Package File
The Binary RPM
And Now…
11. Building Packages: A Simple Example
Creating the Build Directory Structure
Getting the Sources
Creating the Spec File
The Preamble
The %prep Section
The %build Section
The %install Section
The %files Section
The Missing Spec File Sections
Starting the Build
When Things Go Wrong
Problems During the Build
Testing Newly Built Packages
12. rpmbuild Command Reference
rpmbuild — What Does it Do?
rpmbuild -bp — Execute %prep
rpmbuild -bc — Execute %prep, %build
rpmbuild -bi — Execute %prep, %build, %install, %check
rpmbuild -bb — Execute %prep, %build, %install, %check, package (bin)
rpmbuild -ba — Execute %prep, %build, %install, %check, package (bin, src)
rpmbuild -bl — Check %files list
--short-circuit — Force build to start at particular stage
--buildarch <arch> — Perform Build For the <arch> Architecture
--buildos <os> — Perform Build For the <os> Operating System
--sign — Add a Digital Signature to the Package
--test — Create, Save Build Scripts For Review
--clean — Clean up after build
--buildroot <path> — Execute %install using <path> as the root
--timecheck <secs> — Print a warning if files to be packaged are over <secs> old
-vv — Display debugging information
--quiet — Produce as Little Output as Possible
--rcfile <rcfile> — Set alternate rpmrc file to <rcfile>
Other Build-related Commands
rpmbuild --recompile — What Does it Do?
rpmbuild --rebuild — What Does it Do?
13. Inside the Spec File
Comments: Notes Ignored by RPM
Tags: Data Definitions
Package Naming Tags
Descriptive Tags
Dependency Tags
Architecture- and Operating System-Specific Tags
Directory-related Tags
Source and Patch Tags
Scripts: RPM's Workhorse
Build-time Scripts
Install/Erase-time Scripts
Verification-Time Script — The %verifyscript Script
Macros: Helpful Shorthand for Package Builders
The %setup Macro
The %patch Macro
The %files List
Directives For the %files list
File-related Directives
Directory-related Directives
The Lone Directive: %package
-n <string> — Use <string> As the Entire Subpackage Name
Conditionals
The %ifarch Conditional
The %ifnarch Conditional
The %ifos Conditional
The %ifnos Conditional
The %else Conditional
The %endif Conditional
14. Adding Dependency Information to a Package
An Overview of Dependencies
Automatic Dependencies
The Automatic Dependency Scripts
Automatic Dependencies: An Example
The autoreqprov, autoreq, and autoprov Tags — Disable Automatic Dependency Processing
Manual Dependencies
The Requires Tag
The Conflicts Tag
The Provides Tag
To Summarize…
15. Making a Relocatable Package
Why relocatable packages?
The prefix tag: Relocation Central
Relocatable Wrinkles: Things to Consider
%files List Restrictions
Relocatable Packages Must Contain Relocatable Software
The Relocatable Software Is Referenced By Other Software
Building a Relocatable Package
Tying Up the Loose Ends
Test-Driving a Relocatable Package
16. Making a Package That Can Build Anywhere
Using Build Roots in a Package
Some Things to Consider
Having RPM Use a Different Build Area
Setting up a Build Area
Directing RPM to Use the New Build Area
Performing a Build in a New Build Area
Specifying File Attributes
%attr — How Does It Work?
Betcha Thought We Forgot…
17. Adding PGP Signatures to a Package
Why Sign a Package?
Getting Ready to Sign
Preparing PGP: Creating a Key Pair
Preparing RPM
Signing Packages
--sign — Sign a Package At Build-Time
--resign — Replace a Package's Signature(s)
--addsign — Add a Signature To a Package
18. Creating Subpackages
What Are Subpackages?
Why Are They Needed?
Our Example Spec File: Subpackages Galore!
Spec File Changes For Subpackages
The Subpackage's "Preamble"
The %files List
Install- and Erase-time Scripts
Build-Time Scripts: Unchanged For Subpackages
Our Spec File: One Last Look…
Building Subpackages
Giving Subpackages the Once-Over
19. Building Packages for Multiple Architectures and Operating Systems
Architectures and Operating Systems: A Primer
Let's Just Call Them Platforms
What Does RPM Do To Make Multi-Platform Packaging Easier?
Automatic Detection of Build Platform
Automatic Detection of Install Platform
Platform-Dependent Tags
Platform-Dependent Conditionals
Build and Install Platform Detection
Platform-Specific rpmrc Entries
Overriding Platform Information At Build-Time
Overriding Platform Information At Install-Time
optflags — The Other rpmrc File Entry
Platform-Dependent Tags
The excludexxx Tag
The exclusivexxx Tag
Platform-Dependent Conditionals
Common Features of All Conditionals
%ifxxx
%ifnxxx
Hints and Kinks
20. Real-World Package Building
An Overview of Amanda
Initial Building Without RPM
Setting Up A Test Build Area
Getting Software to build
Installing and testing
Initial Building With RPM
Generating patches
Making a first-cut spec file
Getting the original sources unpacked
Getting patches properly applied
Letting RPM do the Building
Letting RPM do the Installing
Testing RPM's Handiwork
Package Building
Creating the %files list
Testing those first packages
Finishing Touches
21. A Guide to the RPM Library API
An Overview of rpmlib
rpmlib Functions
Error Handling
Getting Package Information
Variable Manipulation
rpmrc-Related Information
RPM Database Manipulation
RPM Database Traversal
RPM Database Search
Package Manipulation
Package And File Verification
Dependency-Related Operations
Diagnostic Output Control
Signature Verification
Header Manipulation
Header Entry Manipulation
Header Iterator Support
Example Code
Example #1
Example #2
Example #3
III. Appendixes
A. Format of the RPM File
RPM File Naming Convention
RPM File Format
Parts of an RPM File
The Lead
Wanted: A New RPM Data Structure
The Signature
The Header
The Archive
Tools For Studying RPM Files
Identifying RPM files with the file(1) command
B. The rpmrc File
Using the --showrc Option
Different Places an rpmrc File Resides
/usr/lib/rpmrc
/etc/rpmrc
.rpmrc in the user's login directory
File indicated by the --rcfile option
rpmrc File Syntax
rpmrc File Entries
arch_canon
os_canon
buildarchtranslate
buildostranslate
arch_compat
os_compat
builddir
buildroot
cpiobin
dbpath
defaultdocdir
distribution
excludedocs
ftpport
ftpproxy
messagelevel
netsharedpath
optflags
packager
pgp_name
pgp_path
require_distribution
require_icon
require_vendor
rpmdir
signature
sourcedir
specdir
srcrpmdir
timecheck
tmppath
topdir
vendor
C. Concise RPM Command Reference
Global Options
Informational Options
Query Mode
Package Specification Options To Query Mode
Information Selection Options To Query Mode
Verify Mode
Options To Verify Mode
Install Mode
Options To Install Mode
Upgrade Mode
Options To Upgrade Mode
Erase Mode
Options To Erase Mode
Build Mode
Build Mode Stages
Options To Build Mode
Rebuild Mode
Options To Rebuild Mode
Recompile Mode
Options To Recompile Mode
Resign Mode
Options To Resign Mode
Add Signature Mode
Options To Add Signature Mode
Check Signature Mode
Options To Check Signature Mode
Initialize Database Mode
Options to Initialize database Mode
Rebuild Database Mode
Options to Rebuild Database Mode
D. Available Tags For --queryformat
List of --queryformat Tags
The NAME Tag
The VERSION Tag
The RELEASE Tag
The EPOCH Tag
The SUMMARY Tag
The DESCRIPTION Tag
The BUILDTIME Tag
The BUILDHOST Tag
The INSTALLTIME Tag
The SIZE Tag
The DISTRIBUTION Tag
The VENDOR Tag
The GIF Tag
The XPM Tag
The LICENSE Tag
The PACKAGER Tag
The GROUP Tag
The CHANGELOG Tag
The SOURCE Tag
The PATCH Tag
The URL Tag
The OS Tag
The ARCH Tag
The PREIN Tag
The POSTIN Tag
The PREUN Tag
The POSTUN Tag
The FILENAMES Tag
The FILESIZES Tag
The FILESTATES Tag
The FILEMODES Tag
The FILEUIDS Tag
The FILEGIDS Tag
The FILERDEVS Tag
The FILEMTIMES Tag
The FILEMD5S Tag
The FILELINKTOS Tag
The FILEFLAGS Tag
The ROOT Tag
The FILEUSERNAME Tag
The FILEGROUPNAME Tag
The EXCLUDE Tag
The EXCLUSIVE Tag
The ICON Tag
The SOURCERPM Tag
The FILEVERIFYFLAGS Tag
The ARCHIVESIZE Tag
The PROVIDES Tag
The REQUIREFLAGS Tag
The REQUIRENAME Tag
The REQUIREVERSION Tag
The NOSOURCE Tag
The NOPATCH Tag
The CONFLICTFLAGS Tag
The CONFLICTNAME Tag
The CONFLICTVERSION Tag
The DEFAULTPREFIX Tag
The BUILDROOT Tag
The INSTALLPREFIX Tag
The EXCLUDEARCH Tag
The EXCLUDEOS Tag
The EXCLUSIVEARCH Tag
The EXCLUSIVEOS Tag
The AUTOREQPROV, AUTOREQ, and AUTOPROV Tags
The RPMVERSION Tag
The TRIGGERSCRIPTS Tag
The TRIGGERNAME Tag
The TRIGGERVERSION Tag
The TRIGGERFLAGS Tag
The TRIGGERINDEX Tag
The VERIFYSCRIPT Tag
E. Concise Spec File Reference
Comments
The Preamble
Package Naming Tags
Descriptive Tags
Dependency Tags
Architecture- and Operating System-Specific Tags
Directory-related Tags
Source and Patch Tags
Scriptlets
Build Scriptlets
Install/Erase Scriptlets
%verifyscript Directive
Macros
The %setup Macro
The %patch Macro
The %files List
Directives For the %files list
File-related Directives
Directory-related Directives
%package Directive
The %package -n Option
Conditionals
The %ifarch Conditional
The %ifnarch Conditional
The %ifos Conditional
The %ifnos Conditional
The %else Conditional
The %endif Conditional
F. RPM-related Resources
Where to Get RPM
FTP Sites
What Do I Need?
Where to Talk About RPM
The rpm-list Mailing List
The redhat-list Mailing List
The redhat-digest Mailing List
RPM On the World Wide Web
RPM's License
GNU GENERAL PUBLIC LICENSE
Preamble
GNU GENERAL PUBLIC LICENSE
How to Apply These Terms to Your New Programs
G. An Introduction to PGP
PGP — Privacy for Regular People
Keys your Locksmith Wouldn't Understand
Are RPM Packages Encrypted?
Do All RPM Packages Have Digital Signatures?
So Much to Cover, So Little Time
Installing PGP for RPM's Use
Obtaining PGP
Building PGP
Ready to Go!
Index

List of Tables

2.1. rpm -i Command Syntax
3.1. rpm -e Command Syntax
4.1. rpm -U Command Syntax
5.1. rpm -q Command Syntax
6.1. rpm -V Command Syntax
6.2. Verification Versus File Types
7.1. rpm -K Command Syntax
12.1. rpmbuild Command Syntax

Preface

Linux and RPM — A Brief History

Welcome! This is a book about the Red Hat Package Manager, or RPM Package Manager, known to its friends as simply RPM. The history of RPM is inextricably linked to the history of GNU/Linux, so a bit of GNU/Linux history may be in order. GNU/Linux is a full-featured implementation of a UNIX-like operating system, and has taken the computing world by storm.

And for a good reason — When choosing GNU/Linux, an Intel-based personal computer that had previously been prisoner of the dreaded Windows hourglass is transformed into a fully multitasking, network capable, personal workstation. All for the cost of the time required to download, install, and configure the software.

Of course, if you're not the type to tinker with downloaded software, many companies have created CDROMs containing GNU/Linux and associated software. The amount of tinkering required with these distributions has varied widely. The phrase "You get what you pay for" is never more true than in the area of GNU/Linux distributions.

One distribution bears the curious name "Red Hat Linux". Produced by a company of the same name, this GNU/Linux distribution was different. One of the key decisions a new Linux user needs to make is which of the many different parts of the distribution to install on their system. Most distributions use some sort of menu, making it easy to pick and choose. Red Hat Linux is no different.

But what is different about Red Hat Linux is that the creators of the distribution wanted their customers to have the the ability to make the same choices long after the installation process was over. Some commercial UNIX systems have this capability (called "package management"), and a few GNU/Linux distributors were trying to come up with something similar, but none had at the time the extensive scope present in RPM.

Over time, Red Hat Linux has become the most popular distribution available today. For it to edge out the previous leader (Slackware) in just two years is amazing. There has to be a reason for this kind of success, and a good part of the reason is RPM. But until the first edition of this book, there had been precious little in terms of RPM documentation. You could say that RPM's ease of use has made detailed instructions practically unnecessary, and you'd be right.

However, there are always people that want to know more about their computers, and given the popularity of Red Hat Linux, this alone would have made a book on RPM worthwhile. But there's more to the story than that.

There is a truism in the world of Free Software, that goes something like this: If there's a better solution freely available, use it! RPM is no exception to the rule. Put under the terms of the GNU General Public License (Meaning: RPM cannot be made proprietary by anyone, not even Bill Gates), RPM started to attract the attention of others in the Linux, Unix, and free software communities.

At present, RPM is used by several commercial software companies producing Linux applications. They find that RPM makes it easier to get their products into the hands of their customers. They also find that it can even make the process of building their software easier. (Those of you that develop software for fun and profit, stick around — the second half of this book will show you everything you need to know to get your software "RPM-ized")

People have also ported RPM to several commercial UNIX systems, including DEC's Digital Unix, IBM's AIX, and Silicon Graphics' IRIX. Why? The simple answer is that it makes it easier to install, upgrade, and de-install software. If all these people are using RPM, shouldn't you?

Parts of the book, and who they're for

This book is divided into two major sections. The first section is for anyone that needs to know how to use RPM on their system. Given the state of the GNU/Linux arena today, this could mean just about anyone, including people that are new to GNU/Linux, or even UNIX. So those of you that think that

ls -FAl !* | less
        

is serious magic (or maybe even a typing error), relax — we'll explain everything you'll need to know in the first section.

In the book's second half, we'll be covering all there is to know about building packages using RPM. Since software engineering on GNU/Linux and UNIX systems requires in-depth knowledge of the operating system, available tools, and basic programming concepts, we're going to assume that the reader has sufficient background in these areas. Feel free to browse through the second half, but don't hesitate to seek additional sources of information if you find the going a bit tough.

Acknowledgements

Writing a book is similar to entering a long-term relationship with an obsessive partner. Throughout the nine months it took to write this book, life went on: job changes, births, deaths, and even a hurricane. Throughout it all, the book demanded constant attention. Therefore, I'd like to thank the people that made it possible to focus on the book to the exclusion of nearly everything else. My wife, Deb and son, Matt supported and encouraged me throughout, even when I was little more than a reclusive houseguest hunched over the computer in the study. Additionally, Deb acted as my editor and indexer, eventually reading the book completely three times! Thank you both.

Thanks also to Marc Ewing and Erik Troan, RPM architects extraordinaire. Without their programming savvy, RPM wouldn't be the elegant tool it is. Without their boundless patience, my many questions would have gone unanswered, and this book would have been much less than it is now. I hope you find this book a worthy companion to your programming handiwork.

Rik Faith provided some much-needed information about PMS and PM, two of RPM's ancestors. Thank you!

Finally a great big thank you goes to Jessica and the gang at L'il Dinos, Jennifer and her crew at the Cary Barnes & Noble coffee shop, and Mom and her "kids" at Schlotzsky's Deli in Durham. If all of you hadn't let me sit around for hours writing, this book wouldn't be nearly as fat as it is. And neither would I!

February, 1997 Cary, North Carolina

RPM and Computer Users — How to Use RPM to Effectively Manage Your Computer

Table of Contents

1. An Introduction to Package Management
What are Packages, and Why Manage Them?
Enter the Package
Manage Your Packages, or They Will Manage You
Package Management: How to Do It?
Ancestors of RPM
RPM Design Goals
Make it easy to get packages on and off the system
Make it easy to verify a package was installed correctly
Make it easy for the package builder
Make it start with the original source code
Make it work on different computer architectures
What's in a package?
RPM's Package Labels
Labels And Names: Similar, But Distinct
Package-wide Information
Per-file Information
Let's Get Started
2. Using RPM to Install Packages
rpm -i — What does it do?
Performing dependency checks:
Checking for conflicts:
Performing any tasks required before the install:
Deciding what to do with config files:
Unpacking files from the package and putting them in the proper place:
Performing any tasks required after the install:
Keeping track of what it did:
Performing an Install
URLs — Another Way to Specify Package Files
A warning message you might never see
Two handy options
Getting a bit more feedback with -v
-h: Perfect for the Impatient
Additional options to rpm -i
Getting a lot more information with -vv
--test: Perform Installation Tests Only
--replacepkgs: Install the Package Even If Already Installed
--replacefiles: Install the Package Even If It Replaces Another Package's Files
--nodeps: Do Not Check Dependencies Before Installing Package
--force: The Big Hammer
--excludedocs: Do Not Install Documentation For This Package
--includedocs: Install Documentation For This Package
--prefix <path>: Relocate the package to <path>, if possible
--noscripts: Do Not Execute Pre- and Post-install Scripts
--percent: Not Meant for Human Consumption
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
--root <path>: Use <path> As An Alternate Root
--dbpath <path>: Use <path> To Find RPM Database
--ftpport <port>: Use <port> In FTP-based Installs
--ftpproxy <host>: Use <host> As Proxy In FTP-based Installs
--ignorearch: Do Not Verify Package Architecture
--ignoreos: Do Not Verify Package Operating System
3. Using RPM to Erase Packages
rpm -e — What Does it Do?
Erasing a Package
Getting More Information With -vv
Additional Options
--test — Go Through the Process of Erasing the Package, But Do Not Erase It
--nodeps: Do Not Check Dependencies Before Erasing Package
--noscripts — Do Not Execute Pre- and Post-uninstall Scripts
--rcfile <rcfile> — Read <rcfile> For RPM Defaults
--root <path> — Use <path> As the Root
--dbpath <path>: Use <path> To Find RPM Database
rpm -e and Config files
Watch Out!
4. Using RPM to Upgrade Packages
rpm -U — What Does it Do?
Config file magic
Upgrading a Package
rpm -U's Dirty Little Secret
They're Nearly Identical…
--oldpackage: Upgrade To An Older Version
--force: The Big Hammer
--noscripts: Do Not Execute Install and Uninstall Scripts
5. Getting Information About Packages
rpm -q — What does it do?
The Parts of an RPM Query
Query Commands, Part One: Package Selection
Query Commands, Part Two: Information Selection
Getting a lot more information with -vv
--root <path>: Use <path> As An Alternate Root
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
--dbpath <path>: Use <path> To Find RPM Database
A Few Handy Queries
Finding Config Files Based on a Program Name
Learning More About an Uninstalled Package
Finding Documentation for a Specific Package
Finding Similar Packages
Finding Recently Installed Packages, Part I
Finding Recently Installed Packages, Part II
Finding the Largest Installed Packages
6. Using RPM to Verify Installed Packages
rpm -V — What Does it Do?
What Does it Verify?
When Verification Fails — rpm -V Output
Other Verification Failure Messages
Selecting What to Verify, and How
The Package Label — Verify an Installed Package Against the RPM Database
-a — Verify All Installed Packages Against the RPM Database
-f <file> — Verify the Package Owning <file> Against the RPM Database
-p <file> — Verify Against a Specific Package File
-g <group> — Verify Packages Belonging To <group>
--nodeps: Do Not Check Dependencies During Verification
--noscripts: Do Not Execute Verification Script
--nofiles: Do Not Verify File Attributes
-v — Display Additional Information
-vv — Display Debugging Information
--dbpath <path>: Use <path> To Find RPM Database
--root <path>: Set Alternate Root to <path>
--rcfile <rcfile>: Set Alternate rpmrc file to <rcfile>
We've Lied to You…
RPM Controls What Gets Verified
7. Using RPM to Verify Package Files
rpm -K — What Does it Do?
Pretty Good Privacy: RPM's Assistant
Configuring PGP for rpm -K
Using rpm -K
-v — Display Additional Information
When the Package is Not Signed
When You Are Missing the Correct Public Key
When a Package Just Doesn't Verify
--nopgp — Do Not Verify Any PGP Signatures
-vv — Display Debugging Information
--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File
8. Miscellanea
Other RPM Options
--rebuilddb — Rebuild RPM database
--initdb — Create a New RPM Database
--quiet — Produce as little output as possible
--help — Display a help message
--version — Display the current RPM version
Using rpm2cpio
rpm2cpio — What does it do?
A more real-world example — Listing the files in a package file
Extracting one or more files from a package file
Source Package Files and How To Use Them
A gentle introduction to source code
Do you really need more information than this?
So what can I do with it?
Stick with us!

Chapter 1. An Introduction to Package Management

What are Packages, and Why Manage Them?

To answer that question, let's go back to the basics for a moment. Computers process information. In order for this to happen, there are some prerequisites:

  • A computer (Obviously!).

  • Some information to process (Also obvious!).

  • A program to do the processing (Still pretty obvious!).

Unless these three things come together very little is going to happen, information processing-wise. But each of these items have their own requirements that need to be satisfied before things can get exciting.

Take the computer, for example. While it needs things like electricity and a cool, dry place to operate, it also needs access to the other two items — information and programs — in order to do its thing. The way to get information and programs into a computer is to place them in the computer's mass storage. These days, mass storage invariably means a disk drive. Putting information and programs on the disk drive means that they are stored as files. So much for the computer's part in this.

OK, let's look at the information. Does information have any particular needs? Well, it needs sufficient space on the disk drive, but more importantly, it needs to be in the proper format for the program that will be processing it. That's it for information.

Finally, we have the program. What does it need? Like the information, it needs sufficient disk space on the disk drive. But there are many other things that it may need:

  • It may need information to process, in the correct format, named properly, and in the appropriate area on a disk drive somewhere.

  • It may need one or more configuration files. These are files that control the program's behavior and permit some level of customization. Like the information, these files must be in the proper format, named properly, and in the appropriate area on a disk. We'll be referring to them by their other name — config files — throughout the book.

  • It may need work areas on a disk, named properly, and located in the appropriate area.

  • It may even need other programs, each with their own requirements.

  • Although not strictly required by the program itself, the program may come with one or more files containing documentation. These files can be very handy for the humans trying to get the program to do their bidding!

As you can imagine, this can get pretty complicated. It's not so bad once everything is set up properly, but how do things get set up properly in the first place? There are two possibilities:

  1. After reading the documentation that comes with the program you'd like to use, you copy the various programs, configuration files, and information onto your computer, making sure they are all named correctly, are located in the proper place, and that there is sufficient disk space to hold them all. You make the appropriate changes to the configuration file(s). Finally, you run any setup programs that are necessary, giving them whatever information they require to do their job.

  2. You let the computer do it.

If it seems like the first choice isn't so bad, consider how many files you'll need to keep track of. On a typical Linux system, it's not unusual to have over 20,000 different files. That's a lot of documentation reading, file copying, and configuring! And what happens when you want a newer version of a program? More of the same!

Some people think the second alternative is easier. RPM was made for them.

Enter the Package

When you consider that computers are very good at keeping track of large amounts of data, the idea of giving your computer the job of riding herd over 20,000 files seems like a good one. And that's exactly what package management software does. But what is a "package"?

A package in the computer sense is very similar to a package in the physical sense. Both are methods of keeping related objects together in the same place. Both need to be opened before the contents can be used. Both can have a "packing slip" taped to the side, identifying the contents.

Normally, package management systems take all the various files containing programs, data, documentation, and configuration information, and place them in one specially formatted file — a package file. In the case of RPM, the package file is sometimes called a "package", a ".rpm file", or even an "RPM". All mean the same thing — a package containing software meant to be installed using RPM.

What types of software are normally found in a package? There are no hard and fast rules, but normally a package's contents consist of one of the following types of software:

  • A collection of one or more programs that perform a single well-defined task. This is normally what people think of as an "application". Word processors and programming languages would fit into this category.

  • A specific part of an operating system. Examples might be system initialization scripts, a particular command shell, or the software required to support a web server, for example.

Advantages of a Package

One of the most obvious benefits to having a package is that the package is one easily manageable chunk. If you move it from one place to another, there's no risk of any part getting left behind. But although this is the most obvious advantage, it's not the biggest one.

The biggest advantage is that the package can contain the knowledge about what it takes to install itself on your computer. And if the package contains the steps required to install itself, the package can also contain the steps required to uninstall itself. What used to be a painful manual process is now a straightforward procedure. What used to be a mass of 20,000 files becomes a couple hundred packages.

Manage Your Packages, or They Will Manage You

A couple hundred? Even though the use of packages has decreased the complexity of managing a system by an order of magnitude, it hasn't yet gotten to the level of being a "no-brainer". It's still necessary to keep track of what packages are installed on your system. And if there are some packages that require other packages in order to install or operate correctly, these should be tracked as well.

Packages Lead Active Lives

If you start looking at a computer system as a collection of packages, you'll find that a distinct set of operations will take place on those packages time and time again:

  • New packages are installed. Maybe it's a spreadsheet you'll use to keep track of expenses, or the latest shoot-em-up game, but in either case it's new and you want it.

  • Old packages are replaced with newer versions. Whoever wrote the word processor you use daily, comes out with a new version. You'll probably want to install the new version and remove the old one.

  • Packages are removed entirely. Perhaps that over-hyped strategy game just didn't cut it. You have better things to do with that disk space, so get rid of it!

With this much activity going on, it's easy to lose track of things. What types of package information should be available to keep you informed?

Keeping Track of Packages

Just as there are certain operations that are performed on packages, there are also certain types of information that will make it easier to make sense of the packages installed on your system:

  • Certainly you'd like to be able to see what packages are installed. It's easy to forget if that fax program you tried a few months ago is still installed or not.

  • It would be nice to be able to get more detailed information on a specific package. This might consist of anything from the date the package was installed, to a list of files it installed on your system.

  • Being able to access this information in a variety of ways can be helpful, too. Instead of simply finding out what files a package installed, it might be handy to be able to name a particular file and find out which package installed it.

  • If this amount of detail is possible, then it should be possible to see if the way a package is presently installed varies from the way it was originally installed. It's not at all unusual to make a mistake and delete one file — or a hundred. Being able to tell if one or more packages are missing files could greatly simplify the process of getting an ailing system back on its feet again.

  • Files containing configuration information can be a real headache. If it were possible to pay extra attention to these files and make sure any changes made to them weren't lost, life would certainly be a lot easier.

Package Management: How to Do It?

Well, all that sounds great — easy install, upgrade, and deletion of packages; getting package information presented several different ways; making sure packages are installed correctly; and even tracking changes to config files. But how do you do it?

As mentioned above, the obvious answer is to let the computer do it. Many groups have tried to create package management software. There are two basic approaches:

  1. Some package management systems concentrate on the specific steps required to manipulate a package.

  2. Other package management systems take a different approach, keeping track of the files on the system and manipulating packages by concentrating on the files involved.

Each approach has its good and bad points. In the first method, it's easy to install new packages, somewhat difficult to remove old ones, and almost impossible to obtain any meaningful information about installed packages.

The second method makes it easy to obtain information about installed packages, and fairly easy to install and remove packages. The main problem using this method is that there may not be a well-defined way to execute any commands required during the installation or removal process.

In practice, no package management system uses one approach or the other — all are a mixture of the two. The exact mix and design goals will dictate how well a particular package management system meets the needs of the people using it. At the time Red Hat started work on their Linux distribution, there were a number of package management systems in use, each with a different approach to making package management easier.

Ancestors of RPM

Since this is a book on the Red Hat Package Manager, a good way to see what RPM is all about is to look at the package management software that preceded RPM.

RPP

RPP was used in the first Red Hat Linux distributions. Many of RPP's features would be recognizable to anyone who has worked with RPM. Some of these innovative features are:

  • Simple, one command installation and uninstallation of packages.

  • Scripts that can run before and after installation and uninstallation of packages.

  • Package verification. The files of individual packages can be checked to see that they haven't been modified since they were installed.

  • Powerful querying. The database of packages can be queried for information about installed packages, including file lists, descriptions and more.

While RPP possessed several of the features that were important enough to continue on as parts of RPM today, it had some weaknesses, too:

  • It didn't use "pristine sources". Every program is made up of programming language statements stored in files. This source code is later translated into a binary language that the computer can execute. In the case of RPP, its packages were based on source code that had been modified specifically for RPP, hence the sources weren't pristine. This is a bad idea for a number of fairly technical reasons. Not using pristine sources made it difficult for package developers to keep things straight, particularly if they were building hundreds of different packages.

  • It couldn't guarantee executables were built from packaged sources. The process of building a package for RPP was such that there was no way to ensure the executable programs were built from the source code contained in an RPP source package. Once again, this was a problem for the package builder, especially those who had large numbers of packages to build.

  • It had no support for multiple architectures. As people started using RPP, it became obvious that the package managers that were unable to simplify the process of building packages for more than one architecture, or type of computer, were going to be at a disadvantage. This was a problem, particularly for Red Hat, as they were starting to look at the possibility of creating Linux distributions for other architectures, such as the Digital Alpha.

Even with these problems, RPP was one of the things that made the first Red Hat Linux distributions unique. Its ability to simplify the process of installing software was a real boon to many of Red Hat's customers, particularly those with little experience in Linux.

PMS

While Red Hat was busy with RPP, another group of Linux devotees were hard at work with their package management system. Known as PMS, its development, lead by Rik Faith, attacked the problem of package management from a slightly different viewpoint.

Like RPP, PMS was used to package a Linux distribution. This distribution was known as the BOGUS distribution, and all the software in it was built from original unmodified sources. Any changes that were required were patched in during the processing of building the software. This is the concept of "pristine sources" and is PMS's most important contribution to RPM. The importance of pristine sources can not be overstated. It allows the packager to quickly release new version of software, and to immediately see what changes were made to the software.

The chief disadvantages of PMS were weak querying ability, no package verification, no multiple architecture support, and poor database design.

PM

Later, Rik Faith and Doug Hoffman, working under contract for Red Hat, produced PM. The design combined all the important features of RPP and PM, including one command installation and uninstallation, scripts run before and after installation and uninstallation, package verification, advanced querying, and pristine sources. However it retained RPP's and PM's chief disadvantages: weak database design and no support for multiple architectures.

PM was very close to a viable package management system, but it wasn't quite ready for prime time. It was never used in a commercially available product.

RPM Version 1

With two major forays into package management behind them, Marc Ewing and Erik Troan went to work on a third attempt. This one would be called the Red Hat Package Manager, or RPM.

Although it built on the experiences of PM, PMS, and RPP, RPM was quite different under the hood. Written in the Perl programming language for fast development, the creation of RPM version 1 focused on addressing the flaws of its ancestors. In some cases, the flaws were eliminated, while in others, the problems remained.

Some of the successes of RPM version 1 were:

  • Automatic handling of configuration files. The contents of config files are often changed from what they were in the original package, making it hard for a package manager to know how a particular config file should be handled during installs, upgrades, and erasures. PM made an attempt at config file handling, but in RPM it was improved further. In many respects, this feature is the key to RPM's power and flexibility.

  • Ease of rebuilding large numbers of packages. By making it easy for people who were trying to create a Linux distribution consisting of several hundred packages, RPM was a step in the right direction.

  • It was easy to use. Many of the concepts used in RPP had withstood the test of time and were used in RPM. For instance, the ability to verify the installation of a package was one of the features that set RPP apart. It was adapted and expanded in RPM version 1.

But RPM version 1 wasn't perfect. There were a number of flaws, some of them major:

  • It was slow. While the use of Perl made RPM's development proceed more quickly, it also meant that RPM wouldn't run as quickly as it would have, had it been written in C.

  • Its database design was fragile. Unfortunately, under RPM version 1 it was not unusual for there to be problems with the database. While the approach of dedicating a database to package management was a good idea, the implementation used in RPM version 1 left a lot to be desired.

  • It was big. This is another artifact of using Perl. Normally, RPM's size requirements were not an issue, except for one area. When performing an initial system install, RPM was run from a small, floppy-based system environment. The need to have Perl available meant space on the boot floppies was always a problem.

  • It didn't support multiple architectures (types of computers) well. The need to have a package manager support more than one type of computer hadn't been acknowledged before. With RPM version 1, an initial stab was taken at the problem, but the implementation was incomplete. Nonetheless, RPM had been ported to a number of other computer systems. It was becoming obvious that the issue of multi-architecture support was not going away and had to be addressed.

  • The package file format wasn't extensible. This made it very difficult to add functionality, since any change to the file format would cause older versions of RPM to break.

Even though their Linux distribution was a success, and RPM was much of the reason for it, Marc and Erik knew that some changes were going to be necessary to carry RPM to the next level.

The RPM of Today: Version 2

Looking back on their experiences with RPM version 1, Marc and Erik made a major change to RPM's design: They rewrote it entirely in C. This did wonderful things to RPM's speed and size. Querying the database was quicker now, and there was no need to have Perl around just to do package management.

In addition, the database format was redesigned to improve both performance and reliability. Displaying package information can take as little as a tenth of the time spent in RPM version 1, for example.

Realizing RPM's potential in the non-Linux arena, they also created rpmlib, a library of RPM routines that allow the use of RPM functionality in other programs. RPM's ability to function on more than one architecture was also enhanced. Finally, the package file format was made more extensible, clearing the way for future enhancements to RPM.

So is RPM perfect? No program can ever reach perfection, and RPM is no exception. But as a package manager that can run on several different types of systems, RPM has a lot to offer, and it will only get better. Let's take a look at the design criteria that drove the development of RPM.

RPM Design Goals

The design goals of RPM could best be summed up with the phrase "something for everyone". While the main reason for the existence of RPM was to make it easier for Red Hat to build the several hundred packages that comprised their Linux distribution, it was not the only reason RPM was created. Let's take a look at the various requirements the Red Hat team used in their design of RPM:

Make it easy to get packages on and off the system

As we've seen earlier in this chapter, the act of installing a package can involve many complex steps. Entrusting these steps to a person who may not have the necessary experience is a strategy for failure. So the goal for RPM was to make it as easy as possible for anyone to install packages. The same holds true for removing packages. It is a complex and error-prone operation, and one that RPM should handle for the user.

The other side of this issue is that RPM should give the package builder almost total control in terms of how the package is installed. The reason for this is simple: if the package builders do their homework, their package should install and uninstall properly.

Make it easy to verify a package was installed correctly

Because software problems are a fact of life, the ability to verify the proper installation of a package is vital. If done properly, it should be possible to catch a variety of problems, including things such as missing or modified files.

Make it easy for the package builder

While we're dedicating an entire book to package management, in reality it should be a small portion of the package builder's job. Why? They've got better things to do! If they are the people that are actually creating the software to be packaged, that's where they should be spending the majority of their time.

Even if the package builder isn't actually writing software, they still have better things to do than worry about building packages. For instance, they may be responsible for building many packages. The less time spent on building an individual package translates to more packages that can be built.

Make it start with the original source code

Delving a bit more into the package builder's world, it was deemed important that RPM start with the original, unmodified source code. Why is this so important?

Using the original sources makes it possible to separate the changes required to build the package from any changes implemented to fix bugs, add new features, or anything else. This is a good thing for package builders, since many of them are not the original authors of the programs they package.

This separation makes it easy, months down the road, to know exactly what changes were made in order to get the package to build. This is important when a new version of the packaged software becomes available. Many times it's only necessary to apply the original "package building" changes to the newer software. At worst, the changes provide a starting point to determine what sorts of things might need to be changed in the new version.

Make it work on different computer architectures

One of the tougher things for a package builder to do is to take a program, make it run on more than one type of computer, and distribute packages for each. Because RPM makes it easy to take a program's original source code, add the changes necessary to get it to build, and produce a package for each architecture in one step, it can be pretty handy.

What's in a package?

With all the magical things we've claimed that package management software in general (and RPM in particular) can do, you'd think there was a tiny computer guru bundled in every package. However, the reality is not that magical. Here's a quick overview of the more important parts of an RPM package [1] .

RPM's Package Labels

Every package built for RPM has to have a specific set of information that uniquely identifies it. We call this information a package label. Here are two sample package labels:

  • nls-1.0-1

  • perl-5.001m-4

While these labels look like they have very little in common, in fact they all follow RPM's package labeling convention. There are three different components in every package label. Let's look at each one in order:

Component #1: The Software's Name

Every package label begins with the name of the software. The name may be derived from the name of the application packaged, or it may be a name describing a group of related programs bundled together by the package builder. The software names in the packages listed above are: nls and perl. As you can see, the software name is separated from the rest of the package label by a dash.

Component #2: The Software's Version

Next in the package label is an identifier that describes the version of the software being packaged. If the package builder bundled a number of related programs together, the software version is probably a number of their own choosing. However, if the package consists of one major application, the software version normally comes directly from the application's developer. The actual version specification is quite flexible, as can be seen in the examples above. The versions shown are: 1.0 and 5.001m. A dash separates the software version from the remainder of the package label.

Component #3: The Package's Release

The package release is the most unambiguous part of a package label. It is a number chosen by the package builder. It reflects the number of times the package has been rebuilt using the same version software. Normally, the rebuilds are due to bugs uncovered after the package has been in use for a while. By tradition, the package release starts at 1. The package releases in the example above are: 1 and 4.

Labels And Names: Similar, But Distinct

Package labels are used internally by RPM. For example, if you ask RPM to list every installed package, it will respond with a list of package labels. When a package file is created, part of the filename consists of the package label. There is no technical requirement for this, but it does make it easier to keep track of things.

However, a package file may be renamed, and the new filename won't confuse RPM in the least. That's because the package label is contained within the file. For a fairly technical view of the inside of a package file, refer to Appendix A, Format of the RPM File.

Package-wide Information

Some of the information contained in a package is general in nature. This information includes such items as:

  • The date and time the package was built.

  • A description of the package's contents.

  • The total size of all the files installed by the package.

  • Information that allows the package to be grouped with similar packages.

  • A digital "signature" that can be used to verify the authenticity and integrity of the package. [2]

Per-file Information

Each package also contains information about every file contained in the package. The information includes:

  • The name of every file and where it is to be installed.

  • Each file's permissions.

  • Each file's owner and group specifications.

  • The MD5 checksum of each file. [3]

  • The file's contents.

Let's Get Started

To summarize, a package management system uses the computer to keep track of all the various bits and pieces that comprise an application or an entire operating system. Most package management systems use a specially formatted file to keep everything together in a single, easily manageable entity, or package. Additionally, package management systems tend to provide one or more of the following functions:

  • Installing new packages.

  • Removing old packages.

  • Upgrading from an old package to a new one.

  • Obtaining information about installed packages.

RPM has been designed with Red Hat's past package management experiences in mind. PM and RPP provided most of these functions with varying degrees of success. Marc Ewing and Erik Troan have worked hard to make RPM better than its predecessors in every way. Now it's time to see how they did, and learn how to use RPM!



[1] See Appendix A, Format of the RPM File for complete details on the contents of a .rpm file.

[2] For more information on RPM's signature checking capability, refer to the section called “rpm -K — What Does it Do?”.

[3] We'll discuss MD5 checksums in greater detail in the section called “MD5 Checksum”.

Chapter 2. Using RPM to Install Packages

Table 2.1. rpm -i Command Syntax

rpm -i (or --install) options file1.rpmfileN.rpm
Parameters
file1.rpmfileN.rpm One or more RPM package files (URLs OK)
Install-specific Options Page
-h (or --hash) Print hash marks ("#") during install the section called “-h: Perfect for the Impatient”
--test Perform installation tests only the section called “--test: Perform Installation Tests Only”
--percent Print percentages during install the section called “--percent: Not Meant for Human Consumption”
--excludedocs Do not install documentation the section called “ --excludedocs: Do Not Install Documentation For This Package ”
--includedocs Install documentation the section called “--includedocs: Install Documentation For This Package”
--replacepkgs Replace a package with a new copy of itself the section called “--replacepkgs: Install the Package Even If Already Installed”
--replacefiles Replace files owned by another package the section called “--replacefiles: Install the Package Even If It Replaces Another Package's Files”
--force Ignore package and file conflicts the section called “--force: The Big Hammer”
--noscripts Do not execute pre- and post-install scripts the section called “--noscripts: Do Not Execute Pre- and Post-install Scripts”
--prefix <path> Relocate package to <path> if possible the section called “ --prefix <path>: Relocate the package to <path>, if possible ”
--ignorearch Do not verify package architecture the section called “ --ignorearch: Do Not Verify Package Architecture ”
--ignoreos Do not verify package operating system the section called “ --ignoreos: Do Not Verify Package Operating System ”
--nodeps Do not check dependencies the section called “ --nodeps: Do Not Check Dependencies Before Installing Package ”
--ftpproxy <host> Use <host> as the FTP proxy the section called “ --ftpproxy <host>: Use <host> As Proxy In FTP-based Installs ”
--ftpport <port> Use <port> as the FTP port the section called “ --ftpport <port>: Use <port> In FTP-based Installs ”
General Options Page
-v Display additional information the section called “Getting a bit more feedback with -v
-vv Display debugging information the section called “Getting a lot more information with -vv
--root <path> Set alternate root to <path> the section called “ --root <path>: Use <path> As An Alternate Root ”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File ”
--dbpath <path> Use <path> to find the RPM database the section called “ --dbpath <path>: Use <path> To Find RPM Database ”

rpm -i — What does it do?

Of the many things RPM can do, probably the one that people think of first is the installation of software. As mentioned earlier, installing new software is a complex, error-prone job. RPM turns that process into a single command.

rpm -i (--install is equivalent) installs software that's been packaged into an RPM package file. It does this by:

  • Performing dependency checks.

  • Checking for conflicts.

  • Performing any tasks required before the install.

  • Deciding what to do with config files.

  • Unpacking files from the package and putting them in the proper place.

  • Performing any tasks required after the install.

  • Keeping track of what it did.

Let's go through each of these steps in a bit more detail.

Performing dependency checks:

Some packages will not operate properly unless some other package is installed, too. RPM makes sure that the package being installed will have its dependency requirements met. It will also insure that the package's installation will not cause dependency problems for other already-installed packages.

Checking for conflicts:

RPM performs a number of checks during this phase. These checks look for things like attempts to install an already installed package, attempts to install an older package over a newer version, or the possibility that a file may be overwritten.

Performing any tasks required before the install:

There are cases where one or more commands must be given prior to the actual installation of a package. RPM performs these commands exactly as directed by the package builder, thus eliminating a common source of problems during installations.

Deciding what to do with config files:

One of the features that really sets RPM apart from other package managers, is the way it handles configuration files. Since these files are normally changed to customize the behavior of installed software, simply overwriting a config file would tend to make people angry — all their customizations would be gone! Instead, RPM analyzes the situation and attempts to do "the right thing" with config files, even if they weren't originally installed by RPM! [4]

Unpacking files from the package and putting them in the proper place:

This is the step most people think of when they think about installing software. Each package file contains a list of files that are to be installed, as well as their destination on your system. In addition, many other file attributes, such as permissions and ownerships, are set correctly by RPM.

Performing any tasks required after the install:

Very often a new package requires that one or more commands be executed after the new files are in place. An example of this would be running ldconfig to make new shared libraries accessible.

Keeping track of what it did:

Every time RPM installs a package on your system, it keeps track of the files it installed, in its database. The database contains a wealth of information necessary for RPM to do its job. For example, RPM uses the database when it checks for possible conflicts during an install.

Performing an Install

Let's have RPM install a package. The only thing necessary is to give the command (rpm -i) followed by the name of the package file:

# rpm -i eject-1.2-2.i386.rpm
#
        

At this point, all the steps outlined above have been performed. The package is now installed. Note that the file name need not adhere to RPM's file naming convention:

# mv eject-1.2-2.i386.rpm baz.txt
# rpm -i baz.txt
#
        

In this case, we changed the name of the package file eject-1.2-2.i386.rpm to baz.txt and then proceeded to install the package. The result is identical to the previous install, that is, the eject-1.2-2 package successfully installed. The name of the package file, although normally incorporating the package label, is not used by RPM during the installation process. RPM uses the contents of the package file, which means that even if the file was placed on a DOS floppy and the name truncated, the installation would still proceed normally.

URLs — Another Way to Specify Package Files

If you've surfed the World Wide Web, you've no doubt noticed the way web pages are identified:

http://www.redhat.com/support/docs/rpm/RPM-HOWTO/RPM-HOWTO.html
          

This is called a Uniform Resource Locator, or URL. RPM can also use URLs, although they look a little bit different. Here's one:

ftp://ftp.redhat.com/pub/redhat/code/rpm/rpm-2.3-1.i386.rpm
          

The ftp: signifies that this URL is a File Transfer Protocol URL. As the name implies, this type of URL is used to move files around. The section containing ftp.redhat.com specifies the hostname, or the name of the system where the package file resides.

The remainder of the URL (/pub/redhat/code/rpm/rpm-2.3-1.i386.rpm) specifies the path to the package file, followed by the package file itself.

RPM's use of URLs gives us the ability to install a package located on the other side of the world, with a single command:

# rpm -i ftp://ftp.gnomovision.com/pub/rpms/foobar-1.0-1.i386.rpm
#
          

This command would use anonymous FTP to obtain the foobar version 1.0 package file and install it on your system. Of course, anonymous FTP (no username and password required) is not always available. Therefore, the URL may also contain a username and password preceding the hostname:

ftp://smith:mypass@ftp.gnomovision.com/pub/rpms/foobar-1.0-1.i386.rpm
          

However, entering a password where it can be seen by anyone looking at your screen is a bad idea. So try this format:

ftp://smith@ftp.gnomovision.com/pub/rpms/foobar-1.0-1.i386.rpm
          

RPM will prompt you for your password, and you'll be in business:

# rpm -i ftp://smith@ftp.gnomovision.com/pub/rpms/apmd-2.4-1.i386.rpm
Password for smith@ftp.gnomovision.com: mypass (not echoed)
#
          

After entering a valid password, RPM installs the package.

On some systems, the FTP daemon doesn't run on the standard port 21. Normally this is done for the sake of enhanced security. Fortunately, there is a way to specify a non-standard port in a URL:

ftp://ftp.gnomovision.com:1024/pub/rpms/foobar-1.0-1.i386.rpm
          

This URL will direct the FTP request to port 1024. The --ftpport option is another way to specify the port. This option is discussed later, in the section called “ --ftpport <port>: Use <port> In FTP-based Installs ”.

A warning message you might never see

Depending on circumstances, the following message might be rare or very common. While performing an ordinary install, RPM prints a warning message:

# rpm -i cdp-0.33-100.i386.rpm
warning: /etc/cdp-config saved as /etc/cdp-config.rpmorig
#
          

What does it mean? It has to do with RPM's handling of config files. In the example above, RPM found a file (/etc/cdp-config) that didn't belong to any RPM-installed package. Since the cdp-0.33-100 package contains a file of the same name that is to be installed in the same directory, there is a problem.

RPM solves this the best way it can. It performs two steps:

  1. It renames the original file to cdp-config.rpmorig.

  2. It installs the new cdp-config file that came with the package.

Continuing our example, if we look in /etc, we see that this is exactly what has happened:

# ls -al /etc/cdp*

-rw-r--r--   1 root     root      119 Jun 23 16:00 /etc/cdp-config
-rw-rw-r--   1 root     root       56 Jun 14 21:44 /etc/cdp-config.rpmorig

#
          

This is the best possible solution to a tricky problem. The package is installed with a config file that is known to work. After all, the original file may be for an older, incompatible version of the software. However, the original file is saved so that it can be studied by the system administrator, who can decide whether the original file should be put back into service or not.

Two handy options

There are two options to rpm -i that work so well, and are so useful, you might think they should be RPM's default behavior. They aren't, but using them only requires that you type an extra two characters:

Getting a bit more feedback with -v

Even though rpm -i is doing many things, it's not very exciting, is it? When performing installs, RPM is pretty quiet, unless something goes wrong. However, we can ask for a bit more output by adding -v to the command:

# rpm -iv eject-1.2-2.i386.rpm
Installing eject-1.2-2.i386.rpm
#
          

By adding -v, RPM displayed a simple status line. Using -v is a good idea, particularly if you're going to use a single command to install more than one package:

# rpm -iv *.rpm

Installing eject-1.2-2.i386.rpm
Installing iBCS-1.2-3.i386.rpm
Installing logrotate-1.0-1.i386.rpm

#
          

In this case, there were three .rpm files in the directory. By using a simple wildcard, it's as easy to install one package as it is to install one hundred!

-h: Perfect for the Impatient

Sometimes a package can be quite large. Other than watching the disk activity light flash, there's no assurance that RPM is working, and if it is, how far along it is. If you add -h, RPM will print fifty hash marks ("#") as the install proceeds:

# rpm -ih eject-1.2-2.i386.rpm
##################################################
#
          

Once all fifty hash marks are printed, the package is completely installed. Using -v with -h results in a very nice display, particularly when installing more than one package:

# rpm -ivh *.rpm

eject          ##################################################
iBCS           ##################################################
logrotate      ##################################################

#
          

Additional options to rpm -i

Normally rpm -i, perhaps with the -v and -h, is all you'll need. However, there may be times when a basic install is not going to get the job done. Fortunately, RPM has a wealth of install options to make the tough times a little easier. As with any other powerful tool, you should understand these options before putting them to use. Let's take a look at them:

Getting a lot more information with -vv

Sometimes it's necessary to have even more information than we can get with -v. By adding another v, we can start to see more of RPM's inner workings:

# rpm -ivv eject-1.2-2.i386.rpm

D: installing eject-1.2-2.i386.rpm
Installing eject-1.2-2.i386.rpm
D: package: eject-1.2-2 files test = 0
D: running preinstall script (if any)
D: setting file owners and groups by name (not id)
D: ///usr/bin/eject owned by root (0), group root (0) mode 755
D: ///usr/man/man1/eject.1 owned by root (0), group root (0) mode 644
D: running postinstall script (if any)

#
          

The lines starting with D: have been added by using -vv. The line ending with "files test = 0", means that RPM is actually going to install the package. If the number were non-zero, it would mean that the --test option was present, and RPM would not actually perform the installation. For more information on using --test with rpm -i, see the section called “--test: Perform Installation Tests Only”.

Continuing with the above example, we see that RPM next executes a pre-install script (if there is one), followed by the actual installation of the files in the package. There is one line for each file being installed, and that line shows the filename, ownership, group membership, and permissions (or mode) applied to the file. With larger packages, the output from -vv can get quite lengthy! Finally, RPM runs a post-install script, if one exists for the package. We'll be discussing pre- and post-install scripts in more detail in the section called “--noscripts: Do Not Execute Pre- and Post-install Scripts”.

In the vast majority of cases, it will not be necessary to use -vv. It is normally used by software engineers working on RPM itself, and the output can change without notice. However, it's a handy way to gain insights into RPM's inner workings.

--test: Perform Installation Tests Only

There are times when it's more appropriate to take it slow and not try to install a package right away. RPM provides the --test option for that. As the names implies, it performs all the checks that RPM normally does during an install, but stops short of actually performing the steps necessary to install the package:

# rpm -i --test eject-1.2-2.i386.rpm
#
          

Once again, there's not very much output. This is because the test succeeded; had there been a problem, the output would have been a bit more interesting. In this example, there are some problems:

# rpm -i --test rpm-2.0.11-1.i386.rpm

/bin/rpm conflicts with file from rpm-2.3-1
/usr/bin/gendiff conflicts with file from rpm-2.3-1
/usr/bin/rpm2cpio conflicts with file from rpm-2.3-1
/usr/bin/rpmconvert conflicts with file from rpm-2.3-1
/usr/man/man8/rpm.8 conflicts with file from rpm-2.3-1
error: rpm-2.0.11-1.i386.rpm cannot be installed

#
          

If you'll note the version numbers, we're trying to install an older version of RPM (2.0.11) "on top of" a newer version(2.3). RPM faithfully reported the various file conflicts and summarized with a message saying that the install would not have proceeded, even if --test had not been on the command line.

The --test option will also catch dependency-related problems:

# rpm -i --test blather-7.9-1.i386.rpm

failed dependencies:
        bother >= 3.1 is needed by blather-7.9-1

#
          

Here's a tip for all you script-writers out there: RPM will return a non-zero status if the --test option detects problems…

--replacepkgs: Install the Package Even If Already Installed

The --replacepkgs option is used to force RPM to install a package that it believes to be installed already. This option is normally used if the installed package has been damaged somehow and needs to be fixed up.

To see how the --replacepkgs option works, let's first install some software:

# rpm -iv cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm

#
          

OK, now that we have cdp-0.33-2 installed, let's see what happens if we try to install the same version "on top of" itself:

# rpm -iv cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm
package cdp-0.33-2 is already installed
error: cdp-0.33-2.i386.rpm cannot be installed

#
          

That didn't go very well. Let's see what adding --replacepkgs will do :

# rpm -iv --replacepkgs cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm

#
          

Much better. The original package was replaced by a new copy of itself.

--replacefiles: Install the Package Even If It Replaces Another Package's Files

While the --replacepkgs option permitted a package to be installed "on top of" itself, --replacefiles is used to allow a package to overwrite files belonging to a different package. Sounds strange? Let's go over it in a bit more detail.

One thing that sets RPM apart from many other package managers is that it keeps track of all the files it installs in a database. Each file's database entry contains a variety of information about the file, including a means of summarizing the file's contents. [5] By using these summaries, known as MD5 checksums, RPM can determine if a particular file is going to be replaced by a file with the same name, but different contents. Here's an example:

Package "A" installs a file (we'll call it /bin/foo.bar). Once Package A is installed, foo.bar resides happily in the /bin directory. In the RPM database, there is an entry for /bin/foo.bar, including the file's MD5 checksum.

However, there is a another package, "B". Package B also has a file called foo.bar that it wants to install in /bin. There can't be two files in the same directory with the same name. The files are different; their MD5 checksums do not match. What happens if Package B is installed? Let's find out. Here, we've installed a package:

# rpm -iv cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm

#
          

OK, no problem there. But we have another package to install. In this case, it is a new release of the cdp package. It should be noted that RPM's detection of file conflicts does not depend on the two packages being related. It is strictly based on the name of the file, the directory in which it resides, and the file's MD5 checksum. Here's what happens when we try to install the package:

# rpm -iv cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm
/usr/bin/cdp conflicts with file from cdp-0.33-2
error: cdp-0.33-3.i386.rpm cannot be installed

#
          

What's happening? The package cdp-0.33-2 has a file, /usr/bin/cdp, that it installed. Sure enough, there it is. Let's highlight the size and creation date of the file for future reference:

# ls -al /usr/bin/cdp

-rwxr-xr-x   1 root     root        34460 Feb 25 14:27 /usr/bin/cdp

#
          

The package we just tried to install, cdp-0.33-3 (note the different release), also installs a file cdp in /usr/bin. Since there is a conflict, that means that the two package's cdp files must be different — their checksums don't match. Because of this, RPM won't let the second package install. But with --replacefiles, we can force RPM to let the /usr/bin/cdp from cdp-0.33-3 replace the /usr/bin/cdp from cdp-0.33-2:

# rpm -iv --replacefiles cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm

#
          

Taking a closer look at /usr/bin/cdp, we find that they certainly are different, both in size and creation date:

# ls -al /usr/bin/cdp

-rwxr-xr-x   1 root     root        34444 Apr 24 22:37 /usr/bin/cdp

#
          

File conflicts should be a relatively rare occurrence. They only happen when two packages attempt to install files with the same name but different contents. There are two possible reasons for this to happen:

  • Installing a newer version of a package without erasing the older version. A newer version of a package is a wonderful source of file conflicts against older versions — the filenames remain the same, but the contents change. We used it in our example because it's an easy way to show what happens when there are file conflicts. However, it is usually a bad idea when it comes to doing this as a way to upgrade packages. RPM has a special option for this (rpm -U) that is discussed in Chapter 4, Using RPM to Upgrade Packages.

  • Installing two unrelated packages that each install a file with the same name. This may happen because of poor package design (hence the file residing in more than one package), or a lack of coordination between the people building the packages.

--replacefiles and Config Files

What happens if a conflicting file is a config file that you've sweated over and worked on until it's just right? Will issuing a --replacefiles on a package with a conflicting config file blow all your changes away?

No! RPM won't cook your goose. [6]

It will save any changes you've made, to a config file called <file>.rpmsave. Let's give it a try:

As system administrator, you want to make sure your new users have a rich environment the first time they log in. So you've come up with a really nifty .bashrc file that will be executed whenever they log in. Knowing that everyone will enjoy your wonderful .bashrc file, you place it in /etc/skel. That way, every time a new account is created, your .bashrc will be copied into the new user's login directory.

Not realizing that the .bashrc file you modified in /etc/skel is listed as a config file in a package called (strangely enough) etcskel, you decide to experiment with RPM using the etcskel package. First you try to install it:

# rpm -iv etcskel-1.0-100.i386.rpm

etcskel       /etc/skel/.bashrc conflicts with file from etcskel-1.0-3
error: etcskel-1.0-100.i386.rpm cannot be installed

#
            

Hmmm. That didn't work. Wait a minute! I can add --replacefiles to the command and it should install just fine:

# rpm -iv --replacefiles etcskel-1.0-100.i386.rpm

Installing etcskel-1.0-100.i386.rpm
warning: /etc/skel/.bashrc saved as /etc/skel/.bashrc.rpmsave

#
            

Wait a minute… That's my customized .bashrc! Was it really saved?

# ls -al /etc/skel/

total 8
-rwxr-xr-x   1 root     root          186 Oct 12  1994 .Xclients
-rw-r--r--   1 root     root         1126 Aug 23  1995 .Xdefaults
-rw-r--r--   1 root     root           24 Jul 13  1994 .bash_logout
-rw-r--r--   1 root     root          220 Aug 23  1995 .bash_profile
-rw-r--r--   1 root     root          169 Jun 17 20:02 .bashrc
-rw-r--r--   1 root     root          159 Jun 17 20:46 .bashrc.rpmsave
drwxr-xr-x   2 root     root         1024 May 13 13:18 .xfm
lrwxrwxrwx   1 root     root            9 Jun 17 20:46 .xsession -> .Xclients

# cat /etc/skel/.bashrc.rpmsave

# .bashrc
# User specific aliases and functions
# Modified by the sysadmin
uptime
# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

#
            

Whew! You heave a sigh of relief, and study the new .bashrc to see if the changes need to be integrated into your customized version.

--replacefiles Can Mean Trouble Down the Road

While --replacefiles can make today's difficult install go away, it can mean big headaches in the future. When the time comes for erasing the packages involved in a file conflict, bad things can happen.

What bad things? Well, files can be deleted. Here's how, in three easy steps:

  1. Two packages are installed. When the second package is installed, there is a conflict with a file installed by the first package. Therefore, the --replacefiles option is used to force RPM to replace the conflicting file with the one from the second package.

  2. At some point in the future, the second package is erased.

  3. The conflicting file is gone!

Let's look at an example. First, we install a new package. Next, we take a look at a file it installed, noting the size and creation date.

# rpm -iv cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm

# ls -al /usr/bin/cdp

-rwxr-xr-x   1 root     root        34460 Feb 25 14:27 /usr/bin/cdp

#
            

Next, we try to install a newer release of the same package. It fails:

# rpm -iv cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm
/usr/bin/cdp conflicts with file from cdp-0.33-2
error: cdp-0.33-3.i386.rpm cannot be installed

#
            

So, we use --replacefiles to convince the newer package to install. We note that the newer package installed a file on top of the file originally installed:

# rpm -iv --replacefiles cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm

# ls -al /usr/bin/cdp

-rwxr-xr-x   1 root     root        34444 Apr 24 22:37 /usr/bin/cdp

#
            

The original cdp file, 34,460 bytes long, and dated February 25th, has been replaced with a file with the same name, but 34,444 bytes long from the 24th of April. The original file is long gone.

Next, we erased the package we just installed. [7] Finally, we tried to find the file:

# rpm -e cdp-0.33-3
# ls -al /usr/bin/cdp

ls: /usr/bin/cdp: No such file or directory

#
            

The file is gone. Why is this? The reason is that /usr/bin/cdp from the first package was replaced when the second package was installed using the --replacefiles option. Then, when the second package was erased, the /usr/bin/cdp file was removed, since it belonged to the second package. If the first package had been erased first, there would have been no problem, since RPM would have realized that the first package's file had already been deleted, and would have left the file in place.

The only problem with this state of affairs is that the first package is still installed, except for /usr/bin/cdp. So now there's a partially installed package on the system. What to do? Perhaps it's time to exercise your new-found knowledge by issuing an rpm -i --replacepkgs command to fix up the first package…

--nodeps: Do Not Check Dependencies Before Installing Package

One day it'll happen. You'll be installing a new package, when suddenly, the install bombs:

# rpm -i blather-7.9-1.i386.rpm

failed dependencies:
        bother >= 3.1 is needed by blather-7.9-1

#
          

What happened? The problem is that the package you're installing requires another package to be installed in order for it to work properly. In our example, the blather package won't work properly unless the bother package (and more specifically, bother version 3.1 or later) is installed. Since our system doesn't have an appropriate version of bother installed at all, RPM aborted the installation of blather.

Now, 99 times out of 100, this exactly the right thing for RPM to do. After all, if the package doesn't have everything it needs to work properly, why try to install it? Well, as with everything else in life, there are exceptions to the rule. And that is why there is a --nodeps option.

Adding the --nodeps options to an install command directs RPM to ignore any dependency-related problems and to complete the package installation. Going back to our example above, let's add the --nodeps option to the command line and see what happens:

# rpm -i --nodeps blather-7.9-1.i386.rpm
#
          

The package was installed without a peep. Whether it will work properly is another matter, but it is installed. In general, it's not a good idea to use --nodeps to get around dependency problems. The package builders included the dependency requirements for a reason, and it's best not to second-guess them.

--force: The Big Hammer

Adding --force to an install command is a way of saying "Install it anyway!" In essence, it adds --replacepkgs and --replacefiles to the command. Like a big hammer, --force is an irresistible force [8] that makes things happen. In fact, the only thing that will prevent a --force'ed install from proceeding is a dependency conflict.

And like a big hammer, it pays to fully understand why you need to use --force before actually using it.

--excludedocs: Do Not Install Documentation For This Package

RPM has a number of good features. One of them is the fact that RPM classifies the files it installs into one of three categories:

  1. Config files.

  2. Files containing documentation.

  3. All other files.

RPM uses the --excludedocs option to prevent files classified as documentation from being installed. In the following example, we know that the package contains documentation: specifically, the man page, /usr/man/man1/cdp.1. Let's see how --excludedocs keeps it from being installed:

# rpm -iv --excludedocs cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm

# ls -al /usr/man/man1/cdp.1

ls: /usr/man/man1/cdp.1: No such file or directory

#
          

The primary reason to use --excludedocs is to save on disk space. The savings can be sizeable. For example, on an RPM-installed Linux system, there can be over 5,000 documentation files, using nearly 50 megabytes.

If you like, you can make --excludedocs the default for all installs. To do this, simply add the following line to /etc/rpmrc, .rpmrc in your login directory, or the file specified with the --rcfile (which is discussed in the section called “ --rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File ”) option:

excludedocs: 1

After that, every time an rpm -i command is run, it will not install any documentation files. [9]

--includedocs: Install Documentation For This Package

As the name implies, --includedocs directs RPM to install any files marked as being documentation. This option is normally not required, unless the rpmrc file entry "excludedocs: 1" is included in the referenced rpmrc file. Here's an example. Note that in this example, /etc/rpmrc contains "excludedocs: 1", which directs RPM not to install documentation files:

# ls /usr/man/man1/cdp.1

ls: /usr/man/man1/cdp.1: No such file or directory

# rpm -iv cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm

# ls /usr/man/man1/cdp.1

ls: /usr/man/man1/cdp.1: No such file or directory

#
          

Here we've checked to make sure that the cdp man page did not previously exist on the system. Then after installing the cdp package, we find that the "excludedocs: 1" in /etc/rpmrc did its job: the man page wasn't installed. Let's try it again, this time adding the --includedocs option:

# ls /usr/man/man1/cdp.1

ls: /usr/man/man1/cdp.1: No such file or directory

# rpm -iv --includedocs cdp-0.33-3.i386.rpm

Installing cdp-0.33-3.i386.rpm

# ls /usr/man/man1/cdp.1

-rw-r--r--   1 root     root         4550 Apr 24 22:37 /usr/man/man1/cdp.1

#
          

The --includedocs option overrode the rpmrc file's "excludedocs: 1" entry, causing RPM to install the documentation file.

--prefix <path>: Relocate the package to <path>, if possible

Some packages give the person installing them flexibility in determining where on their system they should be installed. These are known as relocatable packages. A relocatable package differs from a package that cannot be relocated, in only one way — the definition of a default prefix. Because of this, it takes a bit of additional effort to determine if a package is relocatable. But here's an RPM command that can be used to find out: [10]

rpm -qp --queryformat "%{defaultprefix}\n" <packagefile>
          

Just replace <packagefile> with the name of the package file you want to check out. If the package is not relocatable, you'll only see the word (none). If, on the other hand, the command displays a path, that means the package is relocatable. Unless specified otherwise, every file in the package will be installed somewhere below the path specified by the default prefix.

What if you want to specify otherwise? Easy: just use the --prefix option. Let's give it a try:

# rpm -qp --queryformat "%{defaultprefix}\n" cdplayer-1.0-1.i386.rpm

/usr/local

# rpm -i --prefix /tmp/test cdplayer-1.0-1.i386.rpm
#
          

Here we've used our magic query command to determine that the cdplayer package is relocatable. It normally installs below /usr/local, but we wanted to move it around. By adding the --prefix option, we were able to make the package install in /tmp/test. If we take a look there, we'll see that RPM created all the necessary directories to hold the package's files:

# ls -lR /tmp/test/

total 2
drwxr-xr-x   2 root     root         1024 Dec 16 13:21 bin/
drwxr-xr-x   3 root     root         1024 Dec 16 13:21 man/

/tmp/test/bin:
total 41
-rwxr-xr-x   1 root     root        40739 Oct 14 20:25 cdp*
lrwxrwxrwx   1 root     root           17 Dec 16 13:21 cdplay -> /tmp/test/bin/cdp*

/tmp/test/man:
total 1
drwxr-xr-x   2 root     root         1024 Dec 16 13:21 man1/

/tmp/test/man/man1:
total 5
-rwxr-xr-x   1 root     root         4550 Oct 14 20:25 cdp.1*

#
          

--noscripts: Do Not Execute Pre- and Post-install Scripts

Before we talk about the --noscripts option, we need to cover a bit of background. In the section called “Getting a lot more information with -vv, we saw some output from an install using the -vv option. As can be seen, there are two lines that mention pre-install and post-install scripts. When some packages are installed, they may require that certain programs be executed before, after, or before and after the package's files are copied to disk. [11]

The --noscripts option prevents these scripts from being executed during an install. This is a very dangerous thing to do! The --noscripts option is really meant for package builders to use during the development of their packages. By preventing the pre- and post-install scripts from running, a package builder can keep a buggy package from bringing down their development system. Once the bugs are found and eliminated, the --noscripts option is no longer necessary.

--percent: Not Meant for Human Consumption

An option that will probably never be very popular is --percent. This option is meant to be used by programs that interact with the user, perhaps presenting a graphical user interface for RPM. When the --percent option is used, RPM displays a series of numbers. Each number is a percentage that indicates how far along the install is. When the number reaches 100%, the installation is complete.

# rpm -i --percent iBCS-1.2-3.i386.rpm

%f iBCS:1.2:3
%% 0.002140
%% 1.492386
%% 5.296632
%% 9.310026
%% 15.271010
%% 26.217846
%% 31.216000
%% 100.000000
%% 100.000000

#
          

The list of percentages will vary depending on the number of files in the package, but every package ends at 100% when completely installed.

--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc located in your login directory.

This option would be used if there was a need to switch between several sets of RPM defaults. Software developers and package builders will normally be the only people using the --rcfile option. For more information on rpmrc files, see Appendix B, The rpmrc File.

--root <path>: Use <path> As An Alternate Root

Adding --root <path> to an install command forces RPM to assume that the directory specified by <path> is actually the "root" directory. The --root option affects every aspect of the install process, so pre- and post-install scripts are run with <path> as their root directory (using chroot(2), if you must know). In addition, RPM expects its database to reside in the directory specified by the dbpath rpmrc file entry, relative to <path>. [12]

Normally this option is only used during an initial system install, or when a system has been booted off a "rescue disk" and some packages need to be re-installed.

--dbpath <path>: Use <path> To Find RPM Database

In order for RPM to do its handiwork, it needs access to an RPM database. Normally, this database exists in the directory specified by the rpmrc file entry, dbpath. By default, dbpath is set to /var/lib/rpm.

Although the dbpath entry can be modified in the appropriate rpmrc file, the --dbpath option is probably a better choice when the database path needs to be changed temporarily. An example of a time the --dbpath option would come in handy is when it's necessary to examine an RPM database copied from another system. Granted, it's not a common occurrence, but it's difficult to handle any other way.

--ftpport <port>: Use <port> In FTP-based Installs

Back in the section called “URLs — Another Way to Specify Package Files” we showed how RPM can access package files by the use of a URL. We also mentioned that some systems may not use the standard FTP port. In those cases, it's necessary to give RPM the proper port number to use. As we mentioned above, one approach is to embed the port number in the URL itself.

Another approach is to use the --ftpport option. RPM will access the desired port when this option, along with the port number, is added to the command line. In cases where the desired port seldom changes, it may be entered in an rpmrc file by using the ftpport entry. [13]

--ftpproxy <host>: Use <host> As Proxy In FTP-based Installs

Many companies and Internet Service Providers (ISPs) employ various methods to protect their network connections against misuse. One of these methods is to use a system that will process all FTP requests on behalf of the other systems on the company or ISP network. By having a single computer act as a proxy for the other systems, it serves to protect the other systems against any FTP-related misuse.

When RPM is employed on a network with an FTP proxy system, it will be necessary for RPM to direct all its FTP requests to the FTP proxy. RPM will send its FTP requests to the specified proxy system when the --ftpproxy option, along with the proxy hostname, is added to the command line.

In cases where the proxy host seldom changes, it may be entered in an rpmrc file by using the ftpproxy entry. [14]

--ignorearch: Do Not Verify Package Architecture

When a package file is created, RPM specifies the architecture, or type of computer hardware, for which the package was created. This is a good thing, as the architecture is one of the main factors in determining whether a package written for one computer is going to be compatible with another computer.

When a package is installed, RPM uses the arch_compat rpmrc entries in order to determine what are normally considered compatible architectures. Unless you're porting RPM to a new architecture, you shouldn't make any changes to these entries. [15] While RPM attempts to make the right decisions regarding package compatibility, there are times when it errs on the side of conservatism. In those cases, it's necessary to override RPM's decision. The --ignorearch option is used in those cases. When added to the command line, RPM will not perform any architecture-related checking.

Unless you really know what you're doing, you should never use --ignorearch!

--ignoreos: Do Not Verify Package Operating System

When a package file is created, RPM specifies the operating system for which the package was created. This is a good thing as the operating system is one of the main factors in determining whether a package written for one computer is going to be compatible with another computer.

When a package is installed, RPM uses the os_compat rpmrc entries to determine what are normally considered compatible operating systems. Unless you're porting RPM to a new operating system, you shouldn't make any changes to these entries. [16] While RPM attempts to make the right decisions regarding package compatibility, there are times when it errs on the side of conservatism. In those cases, it's necessary to override RPM's decision. The --ignoreos option is used in those cases. When added to the command line, RPM will not perform any operating system-related checking.

Unless you really know what you're doing, you should never use --ignoreos!



[4] Are you interested in what exactly "the right thing" means? the section called “Config file magic” has all the details.

[5] We'll get more into this aspect of RPM in the section called “rpm -V — What Does it Do?” when we discuss rpm -V.

[6] You'll have to do that yourself!

[7] For more information on erasing packages with rpm -e, see Chapter 3, Using RPM to Erase Packages.

[8] No pun intended.

[9] For more information on rpmrc files, refer to Appendix B, The rpmrc File.

[10] We discuss RPM's query commands in Chapter 5, Getting Information About Packages.

[11] It's possible to use RPM's query command to see if a package has pre- or post-install scripts. See the section called “ --scripts — Show Scripts Associated With a Package ” for more information.

[12] For more information on rpmrc file entries, see Appendix B, The rpmrc File.

[13] The use of rpmrc files is described in Appendix B, The rpmrc File.

[14] The use of rpmrc files is described in Appendix B, The rpmrc File.

[15] If you are porting RPM, you'll find more on arch_compat in the section called “ xxx_compat — Define Compatible Architectures ”.

[16] If you are porting RPM, you'll find more on os_compat in the section called “ xxx_compat — Define Compatible Architectures ”.

Chapter 3. Using RPM to Erase Packages

Table 3.1. rpm -e Command Syntax

rpm -e (or --erase) options pkg1pkgN
Parameters
pkg1pkgN One or more installed packages
Erase-specific Options Page
--test Perform erase tests only the section called “ --test — Go Through the Process of Erasing the Package, But Do Not Erase It ”
--noscripts Do not execute pre- and post-uninstall scripts the section called “ --noscripts — Do Not Execute Pre- and Post-uninstall Scripts ”
--nodeps Do not check dependencies the section called “ --nodeps: Do Not Check Dependencies Before Erasing Package ”
General Options Page
-vv Display debugging information the section called “Getting More Information With -vv
--root <path> Set alternate root to <path> the section called “ --root <path> — Use <path> As the Root ”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile> — Read <rcfile> For RPM Defaults ”
--dbpath <path> Use <path> to find the RPM database the section called “ --dbpath <path>: Use <path> To Find RPM Database ”

rpm -e — What Does it Do?

The rpm -e command (--erase is equivalent) removes, or erases, one or more packages from the system. RPM performs a series of steps whenever it erases a package:

  • It checks the RPM database to make sure that no other packages depend on the package being erased.

  • It executes a pre-uninstall script (if one exists).

  • It checks to see if any of the package's config files have been modified. If so, it saves copies of them.

  • It reviews the RPM database to find every file listed as being part of the package, and if they do not belong to another package, deletes them.

  • It executes a post-uninstall script (if one exists).

  • It removes all traces of the package (and the files belonging to it) from the RPM database.

That's quite a bit of activity for a single command. No wonder RPM can be such a time-saver!

Erasing a Package

The most basic erase command is:

# rpm -e eject
#
        

In this case, the eject package was erased. There isn't much in the way of feedback, is there? Could we get more if we add -v?

# rpm -ev eject
#
        

Still nothing. However, there's another option that can be counted on to give a wealth of information. Let's give it a try:

Getting More Information With -vv

By adding -vv to the command line, we can often get a better feel for what's going on inside RPM. The -vv option was really meant for the RPM developers, and its output may change, but it is a great way to gain insight into RPM's inner workings. Let's try it with rpm -e:

# rpm -evv eject

D: uninstalling record number 286040
D: running preuninstall script (if any)
D: removing files test = 0
D: /usr/man/man1/eject.1 - removing
D: /usr/bin/eject - removing
D: running postuninstall script (if any)
D: removing database entry
D: removing name index
D: removing group index
D: removing file index for /usr/bin/eject
D: removing file index for /usr/man/man1/eject.1

#
          

Although -v had no effect on RPM's output, -vv gave us a torrent of output. But what does it tell us?

First, RPM displays the package's record number. The number is normally of use only to people that work on RPM's database code.

Next, RPM executes a "pre-uninstall" script, if one exists. This script can execute any commands required to remove the package before any files are actually deleted.

The "files test = 0" line indicates that RPM is to actually erase the package. If the number had been non-zero, RPM would only be performing a test of the package erasure. This happens when the --test option is used. Refer to the section called “ --test — Go Through the Process of Erasing the Package, But Do Not Erase It ” for more information on the use of the --test option with rpm -e.

The next two lines log the actual removal of the files comprising the package. Packages with many files can result in a lot of output when using -vv!

Next, RPM executes a "post-uninstall" script, if one exists. Like the pre-uninstall script, this script is used to perform any processing required to cleanly erase the package. Unlike the pre-uninstall script, however, the post-uninstall script runs after all the package's files have been removed.

Finally, the last five lines show the process RPM uses to remove every trace of the package from its database. From the messages, we can see that the database contains some per-package data, followed by information on every file installed by the package.

Additional Options

If you're interested in a complex command with lots of options, rpm -e is not the place to look. There just aren't that many different ways to erase a package! But there are a few options you should know about.

--test — Go Through the Process of Erasing the Package, But Do Not Erase It

If you're a bit gun-shy about erasing a package, you can use the --test option first to see what rpm -e would do:

# rpm -e --test bother

removing these packages would break dependencies:
        bother >= 3.1 is needed by blather-7.9-1

#
          

It's pretty easy to see that the blather package wouldn't work very well if bother were erased. To be fair, however, RPM wouldn't have erased the package in this example unless we used the --nodeps option, which we'll discuss shortly.

However, if there are no problems erasing the package, you won't see very much:

# rpm -e --test eject
#
          

We know, based on previous experience, that -v doesn't give us any additional output with rpm -e. However, we do know that -vv works wonders. Let's see what it has to say:

# rpm -evv --test eject

D: uninstalling record number 286040
D: running preuninstall script (if any)
D: would remove files test = 1
D: /usr/man/man1/eject.1 - would remove
D: /usr/bin/eject - would remove
D: running postuninstall script (if any)
D: would remove database entry

#
          

As you can see, the output is similar to that of a regular erase command using the -vv option, with the following exceptions:

  • The "would remove files test = 1" line ends with a non-zero number. This is because --test has been added. If the command hadn't included --test, the number would have been 0, and the package would have been erased.

  • There is a line for each file that RPM would have removed, each one ending with "would remove" instead of "removing".

  • There is only one line at the end, stating: "would remove database entry", versus the multi-line output showing the cleanup of the RPM database during an actual erase.

By using --test in conjunction with -vv, it's easy to see exactly what RPM would do during an actual erase.

--nodeps: Do Not Check Dependencies Before Erasing Package

It's likely that one day while erasing a package, you'll see something like this:

# rpm -e bother

removing these packages would break dependencies:
        bother >= 3.1 is needed by blather-7.9-1

#
          

What happened? The problem is that one or more of the packages installed on your system require the package you're trying to erase. Without it, they won't work properly. In our example, the blather package won't work properly unless the bother package (and more specifically, bother version 3.1 or later) is installed. Since we're trying to erase bother, RPM aborted the erasure.

Now, 99 times out of 100, this is exactly the right thing for RPM to do. After all, if the package is needed by other packages, why try to erase it? As with everything else in life, there are exceptions to the rule. And that is why there is a --nodeps option.

Adding the --nodeps options to an erase command directs RPM to ignore any dependency-related problems, and to erase the package. Going back to our example above, let's add the --nodeps option to the command line and see what happens:

# rpm -e --nodeps bother
#
          

The package was erased without a peep. Whether the blather package will work properly is another matter. In general, it's not a good idea to use --nodeps to get around dependency problems. The package builders included the dependency requirements for a reason, and it's best not to second-guess them.

--noscripts — Do Not Execute Pre- and Post-uninstall Scripts

In the section called “Getting More Information With -vv, we used the -vv option to see what RPM was actually doing when it erased a package. We noted that there were two scripts, a pre-uninstall and a post-uninstall, that were used to execute commands required during the process of erasing a package.

The --noscripts option prevents these scripts from being executed during an erase. This is a very dangerous thing to do! The --noscripts option is really meant for package builders to use during the development of their packages. By preventing the pre- and post-uninstall scripts from running, a package builder can keep a buggy package from bringing down their development system. Once the bugs are found and eliminated, there's very little need to prevent these scripts from running; in fact, doing so can cause problems!

--rcfile <rcfile> — Read <rcfile> For RPM Defaults

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc located in your login directory.

This option would be used if there was a need to switch between several sets of RPM defaults. Software developers and package builders will normally be the only people using the --rcfile option. For more information on rpmrc files, see Appendix B, The rpmrc File.

--root <path> — Use <path> As the Root

Adding --root <path> to an install command forces RPM to assume that the directory specified by <path> is actually the "root" directory. The --root option affects every aspect of the install process, so pre- and post-install scripts are run with <path> as their root directory (using chroot(2), if you must know). In addition, RPM expects its database to reside in the directory specified by the dbpath rpmrc file entry, relative to <path>. [17]

Normally this option is only used during an initial system install, or when a system has been booted off a "rescue disk" and some packages need to be re-installed.

--dbpath <path>: Use <path> To Find RPM Database

In order for RPM to do its handiwork, it needs access to an RPM database. Normally, this database exists in the directory specified by the rpmrc file entry, dbpath. By default, dbpath is set to /var/lib/rpm.

Although the dbpath entry can be modified in the appropriate rpmrc file, the --dbpath option is probably a better choice when the database path needs to be changed temporarily. An example of a time the --dbpath option would come in handy is when it's necessary to examine an RPM database copied from another system. Granted, it's not a common occurrence, but it's difficult to handle any other way.

rpm -e and Config files

If you've made changes to a configuration file that was originally installed by RPM, your changes won't be lost if you erase the package. Say, for example, that we've made changes to /etc/skel/.bashrc (a config file), which was installed as part of the etcskel package. Later, we remove etcskel:

# rpm -e etcskel
#
        

But if we take a look in /etc/skel, look what's there:

# ls -al

total 5
drwxr-xr-x   3 root     root         1024 Jun 17 22:01 .
drwxr-xr-x   8 root     root         2048 Jun 17 19:01 ..
-rw-r--r--   1 root     root          152 Jun 17 21:54 .bashrc.rpmsave
drwxr-xr-x   2 root     root         1024 May 13 13:18 .xfm

#
        

Sure enough: .bashrc.rpmsave is a copy of your modified .bashrc file! Remember, however, that this feature only works with config files. Not sure how to determine which files RPM thinks are config files? Chapter 5, Getting Information About Packages will show you how.

Watch Out!

RPM takes most of the work out of removing software from your system, and that's great. As with everything else in life, however, there's a downside. RPM also makes it easy to erase packages that are critical to your system's continued operation. Here are some examples of packages not to erase:

  • RPM: RPM will happily uninstall itself. No problem — you'll just re-install it with rpm -i… Oops!

  • Bash: The Bourne-again Shell may not be the shell you use, but certain parts of many Linux systems (like the scripts executed during system startup and shutdown) use /bin/sh, which is a symbolic link to /bin/bash. No /bin/bash, no /bin/sh. No /bin/sh, no system!

In many cases, RPM's dependency processing will prevent inadvertent erasures from causing massive problems. However, if you're not sure, use rpm -q to get more information about the package you'd like to erase. [18]



[17] For more information on rpmrc file entries, see Appendix B, The rpmrc File.

[18] See Chapter 5, Getting Information About Packages for more information on rpm -q.

Chapter 4. Using RPM to Upgrade Packages

Table 4.1. rpm -U Command Syntax

rpm -U (or --upgrade)options file1.rpmfileN.rpm
Parameters
file1.rpmfileN.rpm One or more RPM package files (URLs OK)
Upgrade-specific Options Page
-h (or --hash) Print hash marks ("#") during upgrade[a] the section called “-h: Perfect for the Impatient”
--oldpackage Permit "upgrading" to an older package the section called “ --oldpackage: Upgrade To An Older Version ”
--test Perform upgrade tests only[a] the section called “--test: Perform Installation Tests Only”
--excludedocs Do not install documentation[a] the section called “ --excludedocs: Do Not Install Documentation For This Package ”
--includedocs Install documentation[a] the section called “--includedocs: Install Documentation For This Package”
--replacepkgs Replace a package with a new copy of itself[a] the section called “--replacepkgs: Install the Package Even If Already Installed”
--replacefiles Replace files owned by another package[a] the section called “--replacefiles: Install the Package Even If It Replaces Another Package's Files”
--force Ignore package and file conflicts the section called “--force: The Big Hammer”
--percent Print percentages during upgrade[a] the section called “--percent: Not Meant for Human Consumption”
--noscripts Do not execute pre- and post-install scripts the section called “ --noscripts: Do Not Execute Install and Uninstall Scripts ”
--prefix <path> Relocate package to <path> if possible[a] the section called “ --prefix <path>: Relocate the package to <path>, if possible ”
--ignorearch Do not verify package architecture[a] the section called “ --ignorearch: Do Not Verify Package Architecture ”
--ignoreos Do not verify package operating system[a] the section called “ --ignoreos: Do Not Verify Package Operating System ”
--nodeps Do not check dependencies[a] the section called “ --nodeps: Do Not Check Dependencies Before Installing Package ”
--ftpproxy <host> Use <host> as the FTP proxy[a] the section called “ --ftpproxy <host>: Use <host> As Proxy In FTP-based Installs ”
--ftpport <port> Use <port> as the FTP port[a] the section called “ --ftpport <port>: Use <port> In FTP-based Installs ”
General Options Page
-v Display additional information[a] the section called “Getting a bit more feedback with -v
-vv Display debugging information[a] the section called “Getting a lot more information with -vv
--root <path> Set alternate root to <path>[a] the section called “ --root <path>: Use <path> As An Alternate Root ”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile>[a] the section called “ --rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File ”
--dbpath <path> Use <path> to find the RPM database [a] the section called “ --dbpath <path>: Use <path> To Find RPM Database ”

[a] This option behaves identically to the same option used with rpm -i. Please see Chapter 2, Using RPM to Install Packages for more information on this option.

rpm -U — What Does it Do?

If there was one RPM command that could win over friends, it would be RPM's upgrade command. After all, anyone who has ever tried to install a newer version of any software knows what a traumatic experience it can be. With RPM, though, this process is reduced to a single command: rpm -U. The rpm -U command (--upgrade is equivalent) performs two distinct operations:

  1. Installs the desired package.

  2. Erases all older versions of the package, if any exist.

If it sounds to you like rpm -U is nothing more than an rpm -i command (see Chapter 2, Using RPM to Install Packages) followed by the appropriate number of rpm -e commands, (see Chapter 3, Using RPM to Erase Packages) you'd be exactly right. In fact, we'll be referring back to those chapters as we discuss rpm -U, so if you haven't skimmed those chapters yet, you might want to do that now.

While some people might think it's a "cheap shot" to claim that RPM performs an upgrade when in fact it's just doing the equivalent of a couple of other commands, in fact, it's a very smart thing to do. By carefully crafting RPM's package installation and erasure commands to do the work required during an upgrade, it makes RPM more tolerant of misuse by preserving important files even if an upgrade isn't being done.

If RPM had been written with a very "smart" upgrade command, and the install and erase commands couldn't handle upgrade situations at all, installing a package could overwrite a modified configuration file. Likewise, erasing a package would also mean that config files could be erased. Not a good situation! However, RPM's approach to upgrades makes it possible to handle even the most tricky situation — having multiple versions of a package install simultaneously.

Config file magic

While the rpm -i and rpm -e commands each do their part to keep config files straight, it is with rpm -U that the full power of RPM's config file handling shows through. There are no less than six different scenarios that RPM takes into account when handling config files.

In order to make the appropriate decisions, RPM needs information. The information used to decide how to handle config files is a set of three large numbers known as MD5 checksums. An MD5 checksum is produced when a file is used as the input to a complex series of mathematical operations. The resulting checksum has a unique property, in that any change to the file's contents will result in a change to the checksum of that file. [19] Therefore, MD5 checksums are a powerful tool for quickly determining whether two different files have the same contents or not.

In the previous paragraph, we stated that RPM uses three different MD5 checksums to determine what should be done with a config file. The three checksums are:

  1. The MD5 checksum of the file when it was originally installed. We'll call this the original file.

  2. The MD5 checksum of the file as it exists at upgrade time. We'll call this the current file.

  3. The MD5 checksum of the corresponding file in the new package. We'll call this the new file.

Let's take a look at the various combinations of checksums, see what RPM will do because of them, and discuss why. In the following examples, we'll use the letters X, Y, and Z in place of lengthy MD5 checksums.

Original file = X, Current file = X, New file = X

In this case, the file originally installed was never modified. [20] The file in the new version of the package is identical to the file on disk.

In this case, RPM installs the new file, overwriting the original. You may be wondering why go to the trouble of installing the new file if it's just the same as the existing one. The reason is that aspects of the file other than its name and contents might have changed. The file's ownership, for example, might be different in the new version.

Original file = X, Current file = X, New file = Y

The original file has not been modified, but the file in the new package is different. Perhaps the difference represents a bug-fix, or a new feature. It makes no difference to RPM.

In this case, RPM installs the new file, overwriting the original. This makes sense. If it didn't, RPM would never permit newer, modified versions of software to be installed! The original file is not saved, since it had not been changed. A lack of changes here means that no site-specific modifications were made to the file.

Original file = X, Current file = Y, New file = X

Here we have a file that was changed at some point. However, the new file is identical to the existing file prior to the local modifications.

In this case, RPM takes the viewpoint that since the original file and the new file are identical, the modifications made to the original version must still be valid for the new version. It leaves the existing, modified file in place.

Original file = X, Current file = Y, New file = Y

At some point the original file was modified, and those modifications happen to make the file identical to the new file. Perhaps the modification was made to fix a security problem, and the new version of the file has the same fix applied to it.

In this case, RPM installs the new version, overwriting the modified original. The same philosophy used in the first scenario applies here — although the file has not changed, perhaps some other aspect of the file has, so the new version is installed.

Original file = X, Current file = Y, New file = Z

Here the original file was modified at some point. The new file is different from both the original and the modified versions of the original file.

RPM is not able to analyze the contents of the files, and determine what is going on. In this instance, it takes the best possible approach. The new file is known to work properly with the rest of the software in the new package — at least the people building the new package should have insured that it does. The modified original file is an unknown: it might work with the new package, it might not. So RPM installs the new file.

BUT… The existing file was definitely modified. Someone made an effort to change the file, for some reason. Perhaps the information contained in the file is still of use. Therefore, RPM saves the modified file, naming it <file>.rpmsave, and prints a warning, so the user knows what happened:


warning: /etc/skel/.bashrc saved as /etc/skel/.bashrc.rpmsave

            

These five scenarios cover just about every possible circumstance, save one. The missing scenario?

Original file = none, Current file = ??, New file = ??

While RPM doesn't use checksums in this particular case, we'll describe it in those terms, for the sake of consistency. In this instance, RPM had not installed the file originally, so there is no original checksum.

Because the file had not originally been installed as part of a package, there is no way for RPM to determine if the file currently in place had been modified. Therefore, the checksums for the current file and the new file are irrelevant; they cannot be used to clear up the mystery.

When this happens, RPM renames the file to <file>.rpmorig, prints a warning, and installs the new file. This way, any modifications contained in the original file are saved. The system administrator can review the differences between the original and the newly installed files and determine what action should be taken.

As you can see, in the majority of cases RPM will automatically take the proper course of action when performing an upgrade. It is only when config files have been modified and are to be overwritten, that RPM leaves any post-upgrade work for the system administrator. Even in those cases, many times the modified files are not worth saving and can be deleted.

Upgrading a Package

The most basic version of the rpm -U command is simply "rpm -U", followed by the name of a .rpm package file:

# rpm -U eject-1.2-2.i386.rpm
#
        

Here, RPM performed all the steps necessary to upgrade the eject-1.2-2 package, faster than could have been done by hand. As in RPM's install command, Uniform Resource Locators, or URLs, can also be used to specify the package file. [21]

rpm -U's Dirty Little Secret

Well, in the example above, we didn't tell the whole story. There was no older version of the eject package installed. Yes, it's true — rpm -U works just fine as a replacement for the normal install command rpm -i.

This is another, more concrete example of the strength of RPM's method of performing upgrades. Since RPM's install command is smart enough to handle upgrades, RPM's upgrade command is really just another way to specify an install. Some people never even bother to use RPM's install command; they always use rpm -U. Maybe the "-U" should stand for, "Uh, do the right thing"…

They're Nearly Identical…

Given the fact that rpm -U can be used as a replacement to rpm -i, it follows that most of the options available for rpm -U are identical to those used with rpm -i. Therefore, to keep the duplication to a minimum, we'll discuss only those options that are unique to rpm -U, or that behave differently from the same option when used with rpm -i. The table on Table 4.1, “rpm -U Command Syntax” at the start of this chapter shows all valid options to RPM's upgrade command, and indicates which are identical to those used with rpm -i.

--oldpackage: Upgrade To An Older Version

This option might be used a bit more by people that like to stay on the "bleeding edge" of new versions of software, but eventually, everyone will probably need to use it. Usually, the situation plays out like this:

  • You hear about some new software that sounds pretty nifty, so you download the .rpm file and install it.

  • The software is great! It does everything you ask for, and more. You end up using it every day for the next few months.

  • You hear that a new version of your favorite software is available. You waste no time in getting the package. You upgrade the software by using rpm -U. No problem!

  • Fingers arched in anticipation, you launch the new version. Your computer's screen goes blank!

Looks like a bug in the new version. Now what do you do? Hmmm. Maybe you can just "upgrade" to the older version. Let's try to go back to release 2 of cdp-0.33 from release 3:

# rpm -Uv cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm
package cdp-0.33-3 (which is newer) is already installed
error: cdp-0.33-2.i386.rpm cannot be installed

#
          

That didn't work very well. At least it told us just what the problem was — we were trying to upgrade to an older version of a package that is already installed. Fortunately, there's a special option for just this situation: --oldpackage. Let's give it a try:

# rpm -Uv --oldpackage cdp-0.33-2.i386.rpm

Installing cdp-0.33-2.i386.rpm

#
          

By using the --oldpackage option, release 3 of cdp-0.33 is history, and has been replaced by release 2.

--force: The Big Hammer

Adding --force to an upgrade command is a way of saying "Upgrade it anyway!" In essence, it adds --replacepkgs, --replacefiles, and --oldpackage to the command. Like a big hammer, --force is an irresistible force [22] that makes things happen. In fact, the only thing that will prevent a --force'ed upgrade from proceeding is a dependency conflict.

And like a big hammer, it pays to fully understand why you need to use --force before actually using it.

--noscripts: Do Not Execute Install and Uninstall Scripts

The --noscripts option prevents a package's pre- and post-install scripts from being executed. This is no different than the option's behavior when used with RPM's install command. However, there is an additional point to consider when the option is used during an upgrade. The following example uses specially-built packages that display messages when their scripts are executed by RPM:

# rpm -i bother-2.7-1.i386.rpm

This is the bother 2.7 preinstall script
This is the bother 2.7 postinstall script

#
          

In this case, a package has been installed. As expected, its scripts are executed. Next, let's upgrade this package:

# rpm -U bother-3.5-1.i386.rpm

This is the bother 3.5 preinstall script
This is the bother 3.5 postinstall script
This is the bother 2.7 preuninstall script
This is the bother 2.7 postuninstall script

#
          

This is a textbook example of the sequence of events during an upgrade. The new version of the package is installed (as shown by the pre- and post-install scripts being executed). Finally, the previous version of the package is removed (showing the pre- and post-uninstall scripts being executed).

There are really no surprises there — it worked just the way it was meant to. This time, let's use the --noscripts option when the time comes to perform the upgrade:

# rpm -i bother-2.7-1.i386.rpm

This is the bother 2.7 preinstall script
This is the bother 2.7 postinstall script

#
          

Again, the first package is installed, and its scripts are executed. Now let's try the upgrade using the --noscripts option:

# rpm -U --noscripts bother-3.5-1.i386.rpm

This is the bother 2.7 preuninstall script
This is the bother 2.7 postuninstall script

#
          

The difference here is that the --noscripts option prevented the new package's scripts from executing. The scripts from the package being erased were still executed.



[19] Actually, there's a one in 2128 chance a change will go undetected, but for all practical purposes, it's as close to perfect as we can get.

[20] Or, as some sticklers for detail may note, it may have been modified, and subsequently those modifications were undone.

[21] For more information on RPM's use of URLs, please see the section called “URLs — Another Way to Specify Package Files”.

[22] Pun intended.

Chapter 5. Getting Information About Packages

Table 5.1. rpm -q Command Syntax

rpm -q (or --query) options
Package Selection Options Page
pkg1pkgN Query installed package(s) the section called “The Package Label”
-p <file>(or "-") Query package file <file> (URLs OK) the section called “ -p <file> — Query a Specific RPM Package File ”
-f <file> Query package owning <file> the section called “ -f <file> — Query the Package Owning <file>
-a Query all installed packages the section called “-a — Query All Installed Packages”
--whatprovides <x> Query packages providing capability <x> the section called “ --whatprovides <x>: Query the Packages That Provide Capability <x>
-g <group> Query packages belonging to group <group> the section called “ -g <group>: Query Packages Belonging To Group <group>
--whatrequires <x> Query packages requiring capability <x> the section called “ --whatrequires <x>: Query the Packages That Require Capability <x>
Information Selection Options Page
<null> Display full package label the section called “The Package Label”
-i Display summary package information the section called “-i — Display Package Information”
-l Display list of files in package the section called “-l — Display the Package's File List”
-c Display list of configuration files the section called “ -c — Display the Package's List of Configuration Files ”
-d Display list of documentation files the section called “ -d — Display a List of the Package's Documentation ”
-s Display list of files in package, with state the section called “ -s — Display the State of Each File in the Package ”
--scripts Display install, uninstall, verify scripts the section called “ --scripts — Show Scripts Associated With a Package ”
--queryformat (or --qf) Display queried data in custom format the section called “ --queryformat — Construct a Custom Query Response ”
--dump Display all verifiable information for each file the section called “ --dump: Display All Verifiable Information for Each File ”
--provides Display capabilities package provides the section called “ --provides: Display Capabilities Provided by the Package ”
--requires (or -R) Display capabilities package requires the section called “ --requires: Display Capabilities Required by the Package ”
General Options Page
-v Display additional information the section called “ -v — Display Additional Information ”
-vv Display debugging information the section called “ Getting a lot more information with -vv
--root <path> Set alternate root to <path> the section called “ --root <path>: Use <path> As An Alternate Root ”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File ”
--dbpath <path> Use <path> to find the RPM database the section called “ --dbpath <path>: Use <path> To Find RPM Database ”

rpm -q — What does it do?

One of the nice things about using RPM is that the packages you manage don't end up going into some kind of black hole. Nothing would be worse than to install, upgrade, and erase several different packages and not have a clue as to what's on your system. In fact, RPM's query function can help you get out of sticky situations like:

  • You're poking around your system, and you come across a file that you just can't identify. Where did it come from?

  • Your friend sends you a package file, and you have no idea what the package does, what it installs, or where it originally came from.

  • You know that you installed XFree86 a couple months ago, but you don't know what version, and you can't find any documentation on it.

The list could go on, but you get the idea. The rpm -q command is what you need. If you're the kind of person that doesn't like to have more options than you know what to do with, rpm -q might look imposing. But fear not. Once you have a handle on the basic structure of an RPM query, it'll be a piece of cake.

The Parts of an RPM Query

It becomes easy to construct a query command once you understand the individual parts. First is the -q (You can also use --query, if you like). After all, you need to tell RPM what function to perform, right? The rest of a query command consists of two distinct parts: package selection (or what packages you'd like to query), and information selection (or what information you'd like to see). Let's take a look at package selection first:

Query Commands, Part One: Package Selection

The first thing you'll need to decide when issuing an RPM query is what package (or packages) you'd like to query. RPM has several ways to specify packages, so you have quite an assortment to choose from.

The Package Label

In earlier chapters, we discussed RPM's package label, a string that uniquely identifies every installed package. Every label contains three pieces of information:

  1. The name of the packaged software.

  2. The version of the packaged software.

  3. The package's release number.

When issuing a query command using package labels, you must always include the package name. You can also include the version and even the release, if you like. The only restrictions are that each part of the package label specified must be complete, and that if any parts of the package label are missing, all parts to the right must be omitted as well. This second restriction is just a long way of saying that if you specify the release, you must also specify the version as well. Let's look at a few examples.

Say, for instance, you've recently installed a new version of the C libraries, but you can't remember the version number:

# rpm -q libc

libc-5.2.18-1

#
            

In this type of query, RPM returns the complete package label for all installed packages that match the given information. In the example above, if version 5.2.17 of the C libraries was also installed, its package label would have been displayed, too.

In this example, we've included the version as well as the package name:

# rpm -q rpm-2.3

rpm-2.3-1

#
            

Note, however, that RPM is a bit picky about specifying package names. Here are some queries for the C library that won't work:

# rpm -q LibC

package LibC is not installed

#
# rpm -q lib

package lib is not installed

#
# rpm -q "lib*"

package lib* is not installed

#
# rpm -q libc-5

package libc-5 is not installed

#
# rpm -q libc-5.2.1

package libc-5.2.1 is not installed

#
            

As you can see, RPM is case sensitive about package names and cannot match partial names, version numbers, or release numbers. Nor can it use the wildcard characters we've come to know and love. As we've seen, however, RPM can perform the query when more than one field of the package label is present. In the above case, rpm -q libc-5.2.18, or even rpm -q libc-5.2.18-1 would have found the package, libc-5.2.18-1.

Querying based on package labels may seem a bit restrictive. After all, you need to know the exact name of a package in order to perform a query on it. But there are other ways of specifying packages…

-a — Query All Installed Packages

Want lots of information fast? Using the -a option, you can query every package installed on your system. For example:

# rpm -qa

ElectricFence-2.0.5-2
ImageMagick-3.7-2
…
tetex-xtexsh-0.3.3-8
lout-3.06-4

#
            

(On a system installed using RPM, the number of packages can easily number 200 or more; we've deleted most of the output.)

The -a option can produce mountains of output, which makes it a prime candidate for piping through the many Linux/UNIX commands available. One of the prime candidates would be a pager such as more, so that the list of installed packages could be viewed a screenful at a time.

Another handy command to pipe rpm -qa's output through is grep. In fact, using grep, it's possible to get around RPM's lack of built-in wildcard processing:

# rpm -qa | grep -i sysv

SysVinit-2.64-2

#
            

In this example, we were able to find the SysVinit package, even though we didn't have the complete package name, or capitalization.

-f <file> — Query the Package Owning <file>

How many times have you found a program sitting on your system and wondered "what does it do?" Well, if the program was installed by RPM as part of a package, it's easy to find out. Simply use the -f option. Example: You find a strange program called ls in /bin (Okay, it is a contrived example). Wonder what package installed it? Simple!

# rpm -qf /bin/ls

fileutils-3.12-3

#
            

If you happen to point RPM at a file it didn't install, you'll get a message similar to the following:

# rpm -qf .cshrc

file /home/ed/.cshrc is not owned by any package

#
            

A Tricky Detail

It's possible that you'll get the "not owned by any package" message in error. Here's an example of how it can happen:

# rpm -qf /usr/X11/bin/xterm

file /usr/X11/bin/xterm is not owned by any package

#
              

As you can see, we're trying to find out what package the xterm program is part of. The first example failed, which might lead one to believe that xterm really isn't owned by any package.

However, let's look at a directory listing:

# ls -lF /usr

…
lrwxrwxrwx   1 root     root            5 May 13 12:46 X11 -> X11R6/
drwxrwxr-x   7 root     root         1024 Mar 21 00:21 X11R6/
…

#
              

(We've truncated the list; normally /usr is quite a bit more crowded than this.)

The key here is the line ending with "X11 -> X11R6/". This is known as a "symbolic link". It's a way of referring to a file (here, a directory file) by another name. In this case, if we used the path /usr/X11, or /usr/X11R6, it shouldn't make a difference. It certainly doesn't make a difference to programs that simply want access to the file. But it does make a difference to RPM, because RPM doesn't use the filename to access the file. RPM uses the filename as a key into its database. It would be very difficult, if not impossible, to keep track of all the symlinks on a system and try every possible path to a file during a query.

What to do? There are two options:

  1. Make sure you always specify a path free of symlinks. This can be pretty tough, though. An alternative approach is to use namei to track down symlinks:

    # namei /usr/X11/bin/xterm
    
    f: /usr/X11/bin/xterm
     d /
     d usr
     l X11 -> X11R6
       d X11R6
     d bin
     - xterm
    
    #
                        

    It's pretty easy to see the X11 to X11R6 symlink. Using this approach you can enter the non-symlinked path and get the desired results:

    # rpm -qf /usr/X11R6/bin/xterm
    
    XFree86-3.1.2-5
    
    #
                        

  2. Change your directory to the one holding the file you want to query. Even if you use a symlinked path to get there, querying the file should then work as you'd expect:

    # cd /usr/X11/bin
    # rpm -qf xterm
    
    XFree86-3.1.2-5
    
    #
                        

So if you get a "not owned by any package" error, and you think it may not be true, try one of the approaches above.

-p <file> — Query a Specific RPM Package File

Up to now, every means of specifying a package to an RPM query focused on packages that had already been installed. While it's certainly very useful to be able to dredge up information about packages that are already on your system, what about packages that haven't yet been installed? The -p option can do that for you.

One situation where this capability would help, occurs when the name of a package file has been changed. Since the name of the file containing a package has nothing to do with the name of the package (though, by tradition it's nice to name package files consistently), we can use this option to find out exactly what package a file contains:

# rpm -qp foo.bar

rpm-2.3-1

#
            

With one command RPM gives you the answer. [23]

The -p option can also use Uniform Resource Locators to specify package files. See the section called “URLs — Another Way to Specify Package Files” for more information on using URLs.

There's one last trick up -p's sleeve — it can also perform a query by reading a package from standard input. Here's an example:

# cat bother-3.5-1.i386.rpm | rpm -qp -

bother-3.5-1

#
            

We piped the output of cat into RPM. The dash at the end of the command line directs RPM to read the package from standard input.

-g <group>: Query Packages Belonging To Group <group>

When a package is built, the package builder must classify the package, grouping it with other packages that perform similar functions. RPM gives you the ability to query installed packages based on their groups. For example, there is a group known as Base. This group consists of packages that provide low-level structure for a Linux distribution. Let's see what installed packages make up the Base group:

# rpm -qg Base

setup-1.5-1
pamconfig-0.50-5
filesystem-1.2-1
crontabs-1.3-1
dev-2.3-1
etcskel-1.1-1
initscripts-2.73-1
mailcap-1.0-3
pam-0.50-17
passwd-0.50-2
redhat-release-4.0-1
rootfiles-1.3-1
termcap-9.12.6-5

#
            

One thing to keep in mind is that group specifications are case-sensitive. Issuing the command rpm -qg base won't produce any output.

--whatprovides <x>: Query the Packages That Provide Capability <x>

RPM provides extensive support for dependencies between packages. The basic mechanism used is that a package may require what another package provides. The thing that is required and provided can be a shared library's soname. It can also be a character string chosen by the package builder. In any case, it's important to be able to display which packages provide a given capability.

This is just what the --whatprovides option does. When the option, followed by a capability, is added to a query command, RPM will select those packages that provide the capability. Here's an example:

# rpm -q --whatprovides module-info

kernel-2.0.18-5

#
            

In this case, the only package that provides the module-info capability is kernel-2.0.18-5.

--whatrequires <x>: Query the Packages That Require Capability <x>

The --whatrequires option is the logical complement to the --whatprovides option described above. It is used to display which packages require the specified capability. Expanding on the example we started with --whatprovides, let's see which packages require the module-info capability:

# rpm -q --whatrequires module-info

kernelcfg-0.3-2

#
            

There's only one package that requires module-infokernelcfg-0.3-2.

Query Commands, Part Two: Information Selection

After specifying the package (or packages) you wish to query, you'll need to figure out just what information you'd like RPM to retrieve. As we've seen, by default, RPM only returns the complete package label. But there's much more to a package than that. Here, we'll explore every information selection option available to us.

-i — Display Package Information

Adding -i to rpm -q tells RPM to give you some information on the package or packages you've selected. For the sake of clarity, let's take a look at what it gives you and explain what you're looking at:

# rpm -qi rpm

Name        : rpm                    Distribution: Red Hat Linux Vanderbilt
Version     : 2.3                          Vendor: Red Hat Software
Release     : 1                        Build Date: Tue Dec 24 09:07:59 1996
Install date: Thu Dec 26 23:01:51 1996 Build Host: porky.redhat.com
Group       : Utilities/System         Source RPM: rpm-2.3-1.src.rpm
Size        : 631157
Summary     : Red Hat Package Manager
Description :
RPM is a powerful package manager, which can be used to build, install,
query, verify, update, and uninstall individual software packages. A
package consists of an archive of files, and package information, including
name, version, and description.

# 
            

There's quite a bit of information here, so let's go through it entry by entry:

  • Name — The name of the package you queried. Usually (but not always) it bears some resemblance to the name of the underlying software.

  • Version — The version number of the software, as specified by the software's original creator.

  • Release — The number of times a package consisting of this software has been packaged. If the version number should change, the release number should start over again at "1".

As you've probably noticed, these three pieces of information comprise the package label we've come to know and love. Continuing, we have:

  • Install date — This is the time when the package was installed on your system.

  • Group — In our example, this looks suspiciously like a path. If you went searching madly for a directory tree by that name, you'd come up dry — it isn't a set of directories at all.

    When a package builder starts to create a new package, they enter a list of words that describe the software. The list, which goes from least specific to most specific, attempts to categorize the software in a concise manner. The primary use for the group is to enable graphically oriented package managers based on RPM to present packages grouped by function. Red Hat Linux's glint command does this.

  • Size — This is the size (in bytes) of every file in this package. It might make your decision to erase an unused package easier if you see six or more digits here.

  • Summary — This is a concise description of the packaged software.

  • Description — This is a verbose description of the packaged software. Some descriptions might be more, well, descriptive than others, but hopefully it will be enough to clue you in as to the software's role in the greater scheme of things.

  • Distribution — The word "distribution" is really not the best name for this field. "Product" might be a better choice. In any case, this is the name of the product this package is a part of.

  • Vendor — The organization responsible for building this package.

  • Build Date — The time the package was created.

  • Build Host — The name of the computer system that built the package. [24]

  • Source RPM — The process of building a package results in two files:

    1. The package file used to install the packaged software. This is sometimes called the binary package.

    2. The package file containing the source code and other files used to create the binary package file. This is known as the source RPM package file. This is the filename that is displayed in this field.

    Unless you want to make changes to the software, you probably won't need to worry about source packages. But if you do, stick around, because the second part of this book is for you…

-l — Display the Package's File List

Adding -l to rpm -q tells RPM to display the list of files that are installed by the specified package or packages. If you've used ls before, you won't be surprised by RPM's file list.

Here's a look at one of the smaller packages on Red Hat Linux — adduser:

# rpm -ql adduser

/usr/sbin/adduser

#
            

The adduser package consists of only one file, so there's only one filename displayed.

-v — Display Additional Information

In some cases, the -v option can be added to a query command for additional information. The -l option we've been discussing is an example of just such a case. Note how the -v option adds verbosity:

# rpm -qlv adduser

-rwxr-xr-x-     root     root       3894 Feb 25 13:45 /usr/sbin/adduser

#
              

Looks a lot like the output from ls, doesn't it? Looks can be deceiving. Everything you see here is straight from RPM's database. However, the format is identical to ls, so it's more easily understood. If this is Greek to you, consult the ls man page.

-c — Display the Package's List of Configuration Files

When -c is added to an rpm -q command, RPM will display the configuration files that are part of the specified package or packages. As mentioned earlier in the book, config files are important, because they control the behavior of the packaged software. Let's take a look at the list of config files for XFree86:

# rpm -qc XFree86

/etc/X11/fs/config
/etc/X11/twm/system.twmrc
/etc/X11/xdm/GiveConsole
/etc/X11/xdm/TakeConsole
/etc/X11/xdm/Xaccess
/etc/X11/xdm/Xresources
/etc/X11/xdm/Xservers
/etc/X11/xdm/Xsession
/etc/X11/xdm/Xsetup_0
/etc/X11/xdm/chooser
/etc/X11/xdm/xdm-config
/etc/X11/xinit/xinitrc
/etc/X11/xsm/system.xsm
/usr/X11R6/lib/X11/XF86Config

#
            

These are the files you'd want to look at first if you were looking to customize XFree86 for your particular needs. Just like -l, we can also add v for more information:

# rpm -qcv XFree86

-r--r--r---  root  root    423 Mar 21 00:17 /etc/X11/fs/config
…
lrwxrwxrwx-  root  root     30 Mar 21 00:29 /usr/X11R6/lib/X11/XF86Config -> ../../../../etc/X11/XF86Config

#
            

(Note that last file: RPM will display symbolic links, as well.)

-d — Display a List of the Package's Documentation

When -d is added to a query, we get a list of all files containing documentation for the named package or packages. This is a great way to get up to speed when you're having problems with unfamiliar software. As with -c and -l, you'll see either a simple list of filenames, or (if you've added -v) a more comprehensive list. Here's an example that might look daunting at first, but really isn't:

# rpm -qdcf /sbin/dump

/etc/dumpdates
/usr/doc/dump-0.3-5
/usr/doc/dump-0.3-5/CHANGES
/usr/doc/dump-0.3-5/COPYRIGHT
/usr/doc/dump-0.3-5/INSTALL
/usr/doc/dump-0.3-5/KNOWNBUGS
/usr/doc/dump-0.3-5/THANKS
/usr/doc/dump-0.3-5/dump-0.3.announce
/usr/doc/dump-0.3-5/dump.lsm
/usr/doc/dump-0.3-5/linux-1.2.x.patch
/usr/man/man8/dump.8
/usr/man/man8/rdump.8
/usr/man/man8/restore.8
/usr/man/man8/rmt.8
/usr/man/man8/rrestore.8

#
            

Let's take that alphabet soup set of options, one letter at a time:

  • q — Perform a query.

  • d — List all documentation files.

  • c — List all config files.

  • f — Query the package that owns the specified file (/sbin/dump, in this case).

The list of files represents all the documentation and config files that apply to the package owning /sbin/dump.

-s — Display the State of Each File in the Package

Unlike the past three sections, which dealt with a list of files of one type or another, adding -s to a query will list the state of the files that comprise one or more packages. I can hear you out there; you're saying, "What is the state of a file?" For every file that RPM installs, there is an associated state. There are four possible states:

  1. normal — A file in the normal state has not been modified by installing another package on the system.

  2. replaced — Files in the replaced state have been modified by installing another package on the system.

  3. not installed — A file is classified as not installed when it, er, isn't installed! This state is normally seen only if the package was partially installed. An example of a partially installed package would be one that was installed with the --excludedocs option. Using this option, no documentation files would be installed. The RPM database would still contain entries for these missing files, but their state would be not installed.

  4. net shared — The net shared state is used to support client systems that NFS mount portions of their filesystems from a server. Since the server most likely exports filesystems to more than one client, if a client erased a package that contained files on a shared filesystem, other client systems would have incompletely installed packages. The net shared state is used to alert RPM to the fact that a file is on a shared filesystem and should not be erased. Files will be in the net shared state when two things happen:

    1. The netsharedpath rpmrc file entry has been changed from its default (null) value. [25]

    2. The file is to be installed in a directory within a net shared path.

Here's an example showing how file states appear:

# rpm -qs adduser

normal        /usr/sbin/adduser

#
            

(That normal at the start of the line is the state, followed by the file name)

The file state is one of the tools RPM uses to determine the most appropriate action to take when packages are installed or erased.

Now would the average person need to check the states of files? Not really. But if there should be problems, this kind of information can help get things back on track.

--provides: Display Capabilities Provided by the Package

By adding --provides to a query command, we can see the capabilities provided by one or more packages. If the package doesn't provide any capabilities, the --provides option produces no output:

# rpm -q --provides rpm
#
            

However, if a package does provide capabilities, they will be displayed:

# rpm -q --provides foonly

index

#
            

It's important to remember that capabilities are not filenames. In the above example, the foonly package contains no file called index; it's just a character string the package builder chose. This is no different from the following example:

# rpm -q --provides libc

libm.so.5
libc.so.5

#
            

While there might be symlinks by those names in /lib, capabilities are a property of the package, not a file contained in the package!

--requires: Display Capabilities Required by the Package

The --requires option (-R is equivalent) is the logical complement to the --provides option. It displays the capabilities required by the specified package(s). If a package has no requirements, there's no output:

# rpm -q --requires adduser
#
            

In cases where there are requirements, they are displayed as follows:

# rpm -q --requires rpm

libz.so.1
libdb.so.2
libc.so.5

#
            

It's also possible that you'll come across something like this:

# rpm -q --requires blather

bother >= 3.1

#
            

Packages may also be built to require another package. This requirement can also include specific versions. In the example above, the bother package is required by blather; specifically, a version of bother greater than or equal to 3.1.

Here's something worth understanding. Let's say we decide to track down the bother that blather says it requires. If we use RPM's query capabilities, we could use the --whatprovides package selection option to try to find it:

# rpm -q --whatprovides bother

no package provides bother

#
            

No dice. This might lead you to believe that the blather package has a problem. The moral of this story is that, when trying to find out what package fulfills another package's requirements, it's a good idea to also try a simple query using the requirement as a package name. Continuing our example above, let's see if there's a package called bother:

# rpm -q bother

bother-3.5-1

#
            

Bingo! However, if we see what capabilities the bother package provides, we come up dry:

# rpm -q --provides bother
#
            

The reason for the lack of output is that all packages, by default, "provide" their package name (and version).

--dump: Display All Verifiable Information for Each File

The --dump option is used to display every piece of information RPM has on one or more files listed in its database. The information is listed in a very concise fashion. Since the --dump option displays file-related information, the list of files must be chosen by using the -l, -c, or -d options (or some combination thereof):

# rpm -ql --dump adduser

/usr/sbin/adduser 4442 841083888 ca5fa53dc74952aa5b5e3a5fa5d8904b 0100755
root root 0 0 0 X

#
            

What does all this stuff mean? Let's go through it, item-by-item:

  • The /usr/sbin/adduser is simple: it's the name of the file being dump'ed.

  • 4442 is the size of the file, in bytes.

  • How about 841083888? It's the time the file was last modified, in seconds past the Unix zero date of January 1, 1970.

  • The ca5fa53dc74952aa5b5e3a5fa5d8904b is the MD5 checksum of the file's contents, all 128 bits of it.

  • If you guessed 0100755 was the file's mode, you'd be right.

  • The first root represents the file's owner.

  • The second root is the file's group.

  • We'll take the next part (0 0) in one chunk. The first zero shows whether the file is a config file. If zero, as in this case, then the file is not a config file. The next zero shows whether the file is documentation. Again, since there is a zero here, this file isn't documentation, either.

  • The final 0 represents the file's major and minor numbers. These are set only for device special files. Otherwise, it will be zero.

  • If the file were a symlink, the spot taken by the X would contain a path pointing to the linked file.

Normally, the --dump option is used by people that want to extract the file-related information from RPM and process it somehow.

--scripts — Show Scripts Associated With a Package

If you add --scripts (that's two dashes) to a query, you get to see a little bit more of RPM's underlying magic:

# rpm -q --scripts XFree86

preinstall script:
(none)

postinstall script:
/sbin/ldconfig
/sbin/pamconfig --add --service=xdm --password=none --sesslist=none

preuninstall script:
(none)

postuninstall script:
/sbin/ldconfig
if [ "$1" = 0 ] ; then
  /sbin/pamconfig --remove --service=xdm --password=none --sesslist=none
fi

verify script:
(none)


#
            

In this particular case, the XFree86 package has two scripts: one labeled postinstall, and one labeled postuninstall. As you might imagine, the postinstall script is executed just after the package's files have been installed; the postuninstall script is executed just after the package's files have been erased.

Based on the labels in this example, you'd probably imagine that a package can have as many as five different scripts. You'd be right:

  1. The preinstall script, which is executed just before the package's files are installed.

  2. The postinstall script, which is executed just after the package's files are installed.

  3. The preuninstall script, which is executed just before the package's files are removed.

  4. The postuninstall script, which is executed just after the package's files are removed.

  5. And finally, the verify script. While it's easy to figure out the other scripts' functions based on their name, what does a script called verify do? Well, we haven't gotten to it yet, but packages can also be verified for proper installation. This script is used during verification. [26]

Is this something you'll need very often? As in the case of displaying file states, not really. But when you need it, you really need it!

--queryformat — Construct a Custom Query Response

OK, say you're still not satisfied. You'd like some additional information, or you think a different format would be easier on the eyes. Maybe you want to take some information on the packages you've installed and run it through a script for some specialized processing. You can do it, using the --queryformat option. In fact, if you look back at the output of the -i option, RPM was using --queryformat internally. Here's how it works:

On the RPM command line, include --queryformat. Right after that, enter a format string, enclosed in single quotes "'".

The format string can consist of a number of different components:

  • Literal text, including escape sequences.

  • Tags, with optional field width, formatting, and iteration information.

  • Array Iterators.

Let's look at each of these components.

Literal text

Any part of a format string that is not associated with tags or array iterators will be treated as literal text. Literal text is just that: It's text that is printed just as it appears in the format string. In fact, a format string can consist of nothing but literal text, although the output wouldn't tell us much about the packages being queried. Let's give the --queryformat option a try, using a format string with nothing but literal text:

# rpm -q --queryformat 'This is a test!' rpm

This is a test!#
              

The RPM command might look a little unusual, but if you take out the --queryformat option, along with its format string, you'll see this is just an ordinary query of the rpm package. When the --queryformat option is present, RPM will use the text immediately following the option as a format string. In our case, the format string is 'This is a test!'. The single quotes are required. Otherwise, it's likely your shell will complain about some of the characters contained in the average format string.

The output of this command appears on the second line. As we can see, the literal text from the format string was printed exactly as it was entered.

Carriage Control Escape Sequences

Wait a minute. What is that # doing at the end of the output? Well, that's our shell prompt. You see, we didn't direct RPM to move to a new line after producing the output, so the shell prompt ended up being tacked to the end of our output.

Is there a way to fix that? Yes, there is. We need to use an escape sequence. An escape sequence is a sequence of characters that starts with a backslash (\). Escape sequences add carriage control information to a format string. The following escape sequences can be used:

  • \a — Produces a bell or similar alert.

  • \b — Backspaces one character.

  • \f — Outputs a form-feed character.

  • \n — Outputs a newline character sequence.

  • \r — Outputs a carriage return character.

  • \t — Causes a horizontal tab.

  • \v — Causes a vertical tab.

  • \\ — Displays a backslash character.

Based on this list, it seems that a \n escape sequence at the end of the format string will put our shell prompt on the next line:

# rpm -q --queryformat 'This is a test!\n' rpm

This is a test!

#
              

Much better…

Tags

The most important parts of a format string are the tags. Each tag specifies what information is to be displayed and can optionally include field-width, as well as justification and data formatting instructions. [27] But for now, let's look at the basic tag. In fact, let's look at three — the tags that print the package name, version, and release.

Strangely enough, these tags are called NAME, VERSION, and RELEASE. In order to be used in a format string, the tag names must be enclosed in curly braces and preceded by a percent sign. Let's give it a try:

# rpm -q --queryformat '%{NAME}%{VERSION}%{RELEASE}\n' rpm

rpm2.31

#
              

Let's add a dash between the tags and see if that makes the output a little easier to read:

# rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}\n' rpm

rpm-2.3-1

#
              

Now our format string outputs standard package labels.

Field Width and Justification

Sometimes it's desirable to allocate fields of a particular size for a tag. This is done by putting the desired field width between the tag's leading percent sign, and the opening curly brace. Using our package-label-producing format string, let's allocate a 20-character field for the version:

# rpm -q --queryformat '%{NAME}-%20{VERSION}-%{RELEASE}\n' rpm

rpm-                 2.3-1

#
                

The result is a field of 20 characters: 17 spaces, followed by the three characters that make up the version.

In this case, the version field is right justified; that is, the data is printed at the far right of the output field. We can left justify the field by preceding the field width specification with a dash:

# rpm -q --queryformat '%{NAME}-%-20{VERSION}-%{RELEASE}\n' rpm

rpm-2.3                 -1

#
                

Now the version is printed at the far left of the output field. You might be wondering what would happen if the field width specification didn't leave enough room for the data being printed. The field width specification can be considered the minimum width the field will take. If the data being printed is wider, the field will expand to accommodate the data.

Modifiers — Making Data More Readable

While RPM does its best to appropriately display the data from a --queryformat, there are times when you'll need to lend a helping hand. Here's an example. Say we want to display the name of each installed package, followed by the time the package was installed. Looking through the available tags, we see INSTALLTIME. Great! Looks like this will be simple:

# rpm -qa --queryformat '%{NAME} was installed on %{INSTALLTIME}\n'

setup was installed on 845414601
pamconfig was installed on 845414602
filesystem was installed on 845414607
…
rpm was installed on 851659311
pgp was installed on 846027549

#
                

Well, that's a lot of output, but not very useful. What are those numbers? RPM didn't lie -- they're the time the packages were installed. The problem is, the times are being displayed in their numeric form used internally by the operating system, and humans like to see the day, month, year, and so on.

Fortunately, there's a modifier for just this situation. The name of the modifier is :date, and it follows the tag name. Let's try our example again, this time using :date:

# rpm -qa --queryformat '%{NAME} was installed on %{INSTALLTIME:date}\n'

setup was installed on Tue Oct 15 17:23:21 1996
pamconfig was installed on Tue Oct 15 17:23:22 1996
filesystem was installed on Tue Oct 15 17:23:27 1996
…
rpm was installed on Thu Dec 26 23:01:51 1996
pgp was installed on Tue Oct 22 19:39:09 1996

#
                

That sure is a lot easier to understand, isn't it?

Here's a list of the available modifiers:

  • The :date modifier displays dates in human-readable form. It transforms 846027549 into Tue Oct 22 19:39:09 1996.

  • The :perms modifier displays file permissions in an easy-to-read format. It changes -32275 to -rwxr-xr-x-.

  • The :depflags modifier displays the version comparison flags used in dependency processing, in human-readable form. It turns 12 into >=.

  • The :fflags modifier displays a c if the file has been marked as being a configuration file, a d if the file has been marked as being a documentation file, and blank otherwise. Thus, 2 becomes d.

Array Iterators

Until now, we've been using tags that represent single data items. There is, for example, only one package name or installation date for each package. However, there are other tags that can represent many different pieces of data. One such tag is FILENAMES, which can be used to display the names of every file contained in a package.

Let's put together a format string that will display the package name, followed by the name of every file that package contains. We'll try it on the adduser package first, since it contains only one file:

# rpm -q --queryformat '%{NAME}: %{FILENAMES}\n' adduser

adduser: /usr/sbin/adduser

#
              

Hey, not bad — got it on the first try. Now let's try it on a package with more than one file:

# rpm -q --queryformat '%{NAME}: %{FILENAMES}\n' etcskel

etcskel: (array)

#
              

Hmmm. What went wrong? It worked before… Well, it worked before because the adduser package contained only one file. The FILENAMES tag points to an array of names, so when there is more than one file in a package, there's a problem.

But there is a solution. It's called an iterator. An iterator can step through each entry in an array, producing output as it goes. Iterators are created when square braces enclose one or more tags and literal text. Since we want to iterate through the FILENAMES array, let's enclose that tag in the iterator:

# rpm -q --queryformat '%{NAME}: [%{FILENAMES}]\n' etcskel

etcskel: /etc/skel/etc/skel/.Xclients/etc/skel/.Xdefaults/etc/skel/.ba

#
              

There was more output — it went right off the screen in one long line. The problem? We didn't include a newline escape sequence inside the iterator. Let's try it again:

# rpm -q --queryformat '%{NAME}: [%{FILENAMES}\n]' etcskel

etcskel: /etc/skel
/etc/skel/.Xclients
/etc/skel/.Xdefaults
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc
/etc/skel/.xsession

#
              

That's more like it. If we wanted, we could put another file-related tag inside the iterator. If we included the FILESIZES tag, we'd be able to see the name of each file, as well as how big it was:

# rpm -q --queryformat '%{NAME}: [%{FILENAMES} (%{FILESIZES} bytes)\n]' etcskel

etcskel: /etc/skel (1024 bytes)
/etc/skel/.Xclients (551 bytes)
/etc/skel/.Xdefaults (3785 bytes)
/etc/skel/.bash_logout (24 bytes)
/etc/skel/.bash_profile (220 bytes)
/etc/skel/.bashrc (124 bytes)
/etc/skel/.xsession (9 bytes)

#
              

That's pretty nice. But it would be even nicer if the package name appeared on each line, along with the filename and size. Maybe if we put the NAME tag inside the iterator:

# rpm -q --queryformat '[%{NAME}: %{FILENAMES} \
?  (%{FILESIZES} bytes)\n]' etcskel

etcskel: /etc/skel(parallel array size mismatch)#
              

The error message says it all. The FILENAMES and FILESIZES arrays are the same size. The NAME tag isn't even an array. Of course the sizes don't match!

Iterating Single-Entry Tags

If a tag only has one piece of data, it's possible to put it in an iterator and have its one piece of data displayed with every iteration. This is done by preceding the tag name with an equal sign. Let's try it out on our current example:

# rpm -q --queryformat '[%{=NAME}: %{FILENAMES} (%{FILESIZES} bytes)\n]' etcskel

etcskel: /etc/skel (1024 bytes)
etcskel: /etc/skel/.Xclients (551 bytes)
etcskel: /etc/skel/.Xdefaults (3785 bytes)
etcskel: /etc/skel/.bash_logout (24 bytes)
etcskel: /etc/skel/.bash_profile (220 bytes)
etcskel: /etc/skel/.bashrc (124 bytes)
etcskel: /etc/skel/.xsession (9 bytes)

#
                

That's about all there is to format strings. Now, if RPM's standard output doesn't give you what you want, you have no reason to complain. Just --queryformat it!

In Case You Were Wondering…

What's that? You say you don't know what tags are available? You can use RPM's --querytags option. When used as the only option (ie, rpm --querytags), it produces a list of available tags. It should be noted that RPM displays the complete tag name. For instance, RPMTAG_ARCH is the complete name, yet you'll only need to use ARCH in your format string. Here's a partial example of the --querytags option in action:

# rpm --querytags

RPMTAG_NAME
RPMTAG_VERSION
RPMTAG_RELEASE
…
RPMTAG_VERIFYSCRIPT

#
              

Be forewarned: the full list is quite lengthy. At the time this book was written, there were over 70 tags! You'll notice that each tag is printed in uppercase, and is preceded with RPMTAG_. If we were to use that last tag, RPMTAG_VERIFYSCRIPT, in a format string, it could be specified in any of the following ways:


%{RPMTAG_VERIFYSCRIPT}

%{RPMTAG_VerifyScript}

%{RPMTAG_VeRiFyScRiPt}

%{VERIFYSCRIPT}

%{VerifyScript}

%{VeRiFyScRiPt}

              

The only hard-and-fast rule regarding tags is that if you include the RPMTAG_ prefix, it must be all uppercase. The fourth example above shows the traditional way of specifying a tag — prefix omitted, all uppercase. The choice, however, is yours.

One other thing to keep in mind is that not every package will have every type of tagged information available. In cases where the requested information is not available, RPM will display (none) or (unknown). There are also a few tags that, for one reason or another, will not produce useful output when using in a format string. For a comprehensive list of queryformat tags, please see Appendix D, Available Tags For --queryformat.

Getting a lot more information with -vv

Sometimes it's necessary to have even more information than we can get with -v. By adding another v, we can start to see more of RPM's inner workings:

# rpm -qvv rpm

D: opening database in //var/lib/rpm/
D: querying record number 2341208
rpm-2.3-1

#
          

The lines starting with D: have been added by using -vv. We can see where the RPM database is located and what record number contains information on the rpm-2.3-1 package. Following that is the usual output.

In the vast majority of cases, it will not be necessary to use -vv. It is normally used by software engineers working on RPM itself, and the output can change without notice. However, it's a handy way to gain insights into RPM's inner workings.

--root <path>: Use <path> As An Alternate Root

Adding --root <path> to a query command forces RPM to assume that the directory specified by <path> is actually the "root" directory. In addition, RPM expects its database to reside in the directory specified by the dbpath rpmrc file entry, relative to <path>. [28]

Normally this option is only used during an initial system install, or when a system has been booted off a "rescue disk", and some packages need to be re-installed in order to restore normal operation.

--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc, located in your login directory.

This option would be used if there was a need to switch between several sets of RPM options. Software developer and package builders will be the people using --rcfile. For more information on rpmrc files, see Appendix B, The rpmrc File.

--dbpath <path>: Use <path> To Find RPM Database

In order for RPM to do its handiwork, it needs access to an RPM database. Normally, this database exists in the directory specified by the rpmrc file entry, dbpath. By default, dbpath is set to /var/lib/rpm.

Although the dbpath entry can be modified in the appropriate rpmrc file, the --dbpath option is probably a better choice when the database path needs to be changed temporarily. An example of a time the --dbpath option would come in handy is when it's necessary to examine an RPM database copied from another system. Granted, it's not a common occurrence, but it's difficult to handle any other way.

A Few Handy Queries

Below are some examples of situations you might find yourself in, and ways you can use RPM to get the information you need. Keep in mind that these are just examples. Don't be afraid to experiment!

Finding Config Files Based on a Program Name

You're setting up a new system, and you'd like to implement some system-wide aliases for people using the Bourne Again SHell, bash. The problem is you just can't remember the name of the system-wide initialization file used by bash, or where it resides:

# rpm -qcf /bin/bash

/etc/bashrc

#
          

Rather than spending time trying to hunt down the file, RPM finds it for you in seconds.

Learning More About an Uninstalled Package

Practically any option can be combined with -qp to extract information from a .rpm file. Let's say you have an unknown .rpm file, and you'd like to know a bit more before installing it:

# rpm -qpil foo.bar

Name        : rpm                  Distribution: Red Hat Linux Vanderbilt
Version     : 2.3                        Vendor: Red Hat Software
Release     : 1                      Build Date: Tue Dec 24 09:07:59 1996
Install date: (none)                 Build Host: porky.redhat.com
Group       : Utilities/System       Source RPM: rpm-2.3-1.src.rpm
Size        : 631157
Summary     : Red Hat Package Manager
Description :
RPM is a powerful package manager, which can be used to build, install,
query, verify, update, and uninstall individual software packages. A
package consists of an archive of files, and package information,
including name, version, and description.
/bin/rpm
/usr/bin/find-provides
/usr/bin/find-requires
/usr/bin/gendiff
/usr/bin/rpm2cpio
/usr/doc/rpm-2.3-1
…
/usr/src/redhat/SOURCES
/usr/src/redhat/SPECS
/usr/src/redhat/SRPMS

# 
          

By displaying the package information, we know that we have a package file containing RPM version 2.3. We can then peruse the file list, and see exactly what it would install before installing it.

Finding Documentation for a Specific Package

Picking on bash some more, you realize that your knowledge of the software is lacking. You'd like to see when it was installed on your system, and what documentation is available for it:

# rpm -qid bash

Name        :bash                   Distribution: Red Hat Linux (Picasso)
Version     :1.14.6                       Vendor: Red Hat Software
Release     :2                        Build Date: Sun Feb 25 13:59:26 1996
Install date:Mon May 13 12:47:22 1996 Build Host: porky.redhat.com
Group       :Shells                   Source RPM: bash-1.14.6-2.src.rpm
Size        :486557
Description :GNU Bourne Again Shell (bash)
/usr/doc/bash-1.14.6-2
/usr/doc/bash-1.14.6-2/NEWS
/usr/doc/bash-1.14.6-2/README
/usr/doc/bash-1.14.6-2/RELEASE
/usr/info/bash.info.gz
/usr/man/man1/bash.1

#
          

You never realized that there could be so much documentation for a shell!

Finding Similar Packages

Looking at bash's information, we see that it belongs to the group "Shells". You're not sure what other shell packages are installed on your system. If you can find other packages in the "Shells" group, you'll have found the other installed shells:

# rpm -qa --queryformat '%10{NAME} %20{GROUP}\n' | grep -i shells

       ash               Shells
      bash               Shells
       csh               Shells
        mc               Shells
      tcsh               Shells

#
          

Now you can query each of these packages, and learn more about them, too. [29]

Finding Recently Installed Packages, Part I

You remember installing a new package a few days ago. All you know for certain is that the package installed a new command in the /bin directory. Let's try to find the package:

# find /bin -type f -mtime -14 | rpm -qF

rpm-2.3-1

#
          

Looks like RPM version 2.3 was installed sometime in the last two weeks.

Finding Recently Installed Packages, Part II

Another way to see which packages were recently installed is to use the --queryformat option:

# rpm -qa --queryformat '%{installtime} %{name}-%{version}-%{release} %{installtime:date}\n' | sort -nr +1 | sed -e 's/^[^ ]* //'

rpm-devel-2.3-1 Thu Dec 26 23:02:05 1996
rpm-2.3-1 Thu Dec 26 23:01:51 1996
pgp-2.6.3usa-2 Tue Oct 22 19:39:09 1996
…
pamconfig-0.50-5 Tue Oct 15 17:23:22 1996
setup-1.5-1 Tue Oct 15 17:23:21 1996

#
          

By having RPM include the installation time in numeric form, it was simple to sort the packages and then use sed to remove the user-unfriendly numeric time.

Finding the Largest Installed Packages

Let's say that you're running low on disk space, and you'd like to see what packages you have installed, along with the amount of space each package takes up. You'd also like to see the largest packages first, so you can get back as much disk space as possible:

# rpm -qa --queryformat '%{name}-%{version}-%{release} %{size}\n' | sort -nr +1

kernel-source-2.0.18-5  20608472
tetex-0.3.4-3  19757371
emacs-el-19.34-1  12259914
…
rootfiles-1.3-1  3494
mkinitrd-1.0-1  1898
redhat-release-4.0-1  22

#
          

If you don't build custom kernels, or use TeX, it's easy to see how much space could be reclaimed by removing those packages.



[23] On most Linux systems, the file command can be used to obtain similar information. See Appendix A, Format of the RPM File for details on how to add this capability to your system's file command.

[24] Note to software packagers: Choose your build machine names wisely! A silly or offensive name might be embarrassing…

[25] For more information on rpmrc file entries, please refer to Appendix B, The rpmrc File.

[26] For more information on package verification, please see the section called “rpm -V — What Does it Do?”.

[27] RPM uses printf to do --queryformat formatting. Therefore, you can use any of the printf format modifiers discussed in the printf(3) man page.

[28] For more information on rpmrc file entries, see Appendix B, The rpmrc File.

[29] Did you see this example and say to yourself, "Hey, they could've used the -g option to query for that group directly"? If you did, you've been paying attention. This is a more general way of searching the RPM database for information: we just happened to search by group in this example.

Chapter 6. Using RPM to Verify Installed Packages

Table 6.1. rpm -V Command Syntax

rpm -V or (--verify, or -y) options
Package Selection Options Page
pkg1pkgN Verify named package(s) the section called “ The Package Label — Verify an Installed Package Against the RPM Database ”
-p <file> Verify against package file <file> the section called “ -p <file> — Verify Against a Specific Package File ”
-f <file> Verify package owning <file> the section called “ -f <file> — Verify the Package Owning <file> Against the RPM Database ”
-a Verify all installed packages the section called “ -a — Verify All Installed Packages Against the RPM Database ”
-g <group> Verify packages belonging to group <group> the section called “ -g <group> — Verify Packages Belonging To <group>
Verify-specific Options Page
--noscripts Do not execute verification script the section called “ --noscripts: Do Not Execute Verification Script ”
--nodeps Do not verify dependencies the section called “ --nodeps: Do Not Check Dependencies During Verification ”
--nofiles Do not verify file attributes the section called “ --nofiles: Do Not Verify File Attributes ”
General Options Page
-v Display additional information the section called “-v — Display Additional Information”
-vv Display debugging information the section called “-vv — Display Debugging Information”
--root <path> Set alternate root to <path> the section called “ --root <path>: Set Alternate Root to <path>
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile>: Set Alternate rpmrc file to <rcfile>
--dbpath <path> Use <path> to find the RPM database the section called “ --dbpath <path>: Use <path> To Find RPM Database ”

rpm -V — What Does it Do?

From time to time, it's necessary to make sure that everything on your system is "OK". Are you sure the packages you've installed are still configured properly? Have there been any changes made that you don't know about? Did you mistakenly start a recursive delete in /usr and now have to assess the damage?

RPM can help. It can alert you to changes made to any of the files installed by RPM. Also, if a package requires capabilities provided by another package, it can make sure the other package is installed, too.

The command rpm -V (The options -y and --verify are equivalent) verifies an installed package. Before we see how this is done, let's take a step back and look at the big picture.

Every time a package is installed, upgraded, or erased, the changes are logged in RPM's database. It's necessary for RPM to keep track of this information; otherwise it wouldn't be able to perform these operations correctly. You can think of the RPM database (and the disk space it consumes) as being the "price of admission" for the easy package management that RPM provides. [30]

The RPM database reflects the configuration of the system on which it resides. When RPM accesses the database to see how files should be manipulated during an install, upgrade, or erase, it is using the database as a mirror of the system's configuration.

However, we can also use the system configuration as a mirror of the RPM database. What does this "backward" view give us? What purpose would be served?

The purpose would be to see if the system configuration accurately reflects the contents of the RPM database. If the system configuration doesn't match the database, then we can reach one of two conclusions:

  1. The RPM database has become corrupt. The system configuration is unchanged.

  2. The RPM database is intact. The system configuration has changed.

While it would be foolish to state that an RPM database has never become corrupt, it is a sufficiently rare occurrence that the second conclusion is much more likely. So RPM gives us a powerful verification tool, essentially for free.

What Does it Verify?

It would be handy if RPM did nothing more than verify that every file installed by a package actually exists on your system. In reality, RPM does much more. It makes sure that if a package depends on other packages to provide certain capabilities, the necessary packages are, in fact, installed. If the package builder created one, RPM will also run a special verification script that can verify aspects of the package's installation that RPM cannot.

Finally, every file installed by RPM is examined. No less than nine different attributes of each file can be checked. Here is the list of attributes:

  • Owner

  • Group

  • Mode

  • MD5 Checksum

  • Size

  • Major Number

  • Minor Number

  • Symbolic Link String

  • Modification Time

Let's take a look at each of these attributes and why they are good things to check:

File Ownership

Most operating systems today keep track of each file's creator. This is done primarily for resource accounting. Linux and UNIX also use file ownership to help determine access rights to the file. In addition, some files, when executed by a user, can temporarily change the user's ID, normally to a more privileged ID. Therefore, any change of file ownership may have far reaching effects on data security and system availability.

File Group

In a similar manner to file ownership, a "group" specification is attached to each file. Primarily used for determining access rights, a file's group specification can also become a user's group ID, should that user execute the file's contents. Therefore, any changes in a file's group specification are important, and should be monitored.

File Mode

Encompassing the file's "permissions", the mode is a set of bits that specifies permitted access for the file's owner, group members, and everyone else. Even more important are two additional bits that determine whether a user's group or user ID should be changed if they execute the program contained in the file. Since these little bombshells can let any user become root for the duration of the program, it pays to be extra careful with a file's permissions.

MD5 Checksum

The MD5 checksum of a file is simply a 128-bit number that is mathematically derived from the contents of the file. The MD5 algorithm was designed by Ron Rivest, the "R" in the popular RSA public-key encryption algorithm. The "MD" in "MD5" stands for Message Digest, which is a pretty accurate description of what it does.

Unlike literary digests, an MD5 checksum conveys no information about the contents of the original file. However, it possesses one unique trait:

  • Any change to the file, no matter how small, results in a change to the MD5 checksum. [31]

RPM creates MD5 checksums of all files it manipulates, and stores them in its database. For all intents and purposes, if one of these files is changed, the MD5 checksum will change, and RPM will detect it.

File Size

As if the use of MD5 isn't enough, RPM also keeps track of file sizes. A difference of even one byte more or less will not go unnoticed.

Major Number

Device character and block files possess a major number. The major number is used to communicate information to the device driver associated with the special file. For instance, under Linux the special files for SCSI disk drives should have a major number of 8, while the major number for an IDE disk drive's special file would be 3. As you can imagine, any change to a file's major number can have disastrous effects, and is tracked by RPM.

Minor Number

A file's minor number is similar in concept to the major number, but conveys different information to the device driver. In the case of disk drives, this information can consist of a unit identifier. Should the minor number change, RPM will detect it.

Symbolic Link

If the file in question is really a symbolic link, the text string containing the name of the linked-to file is checked.

Modification Time

Most operating systems keep track of the date and time that a file was last modified. RPM uses this to its advantage by keeping modification times in its database.

When Verification Fails — rpm -V Output

When verifying a package, RPM produces output only if there is a verification failure. When a file fails verification, the format of the output is a bit cryptic, but it packs all the information you need into one line per file. Here is the format:


SM5DLUGT c <file>

        

Where:

  • S is the file size.

  • M is the file's mode.

  • 5 is the MD5 checksum of the file.

  • D is the file's major and minor numbers.

  • L is the file's symbolic link contents.

  • U is owner of the file.

  • G is the file's group.

  • T is the modification time of the file.

  • c appears only if the file is a configuration file. This is handy for quickly identifying config files, as they are very likely to change, and therefore, very unlikely to verify successfully.

  • <file> is the file that failed verification. The complete path is listed to make it easy to find.

It's unlikely that every file attribute will fail to verify, so each of the eight attribute flags will only appear if there is a problem. Otherwise, a "." will be printed in that flag's place. Let's look at an example or two:


.M5....T   /usr/X11R6/lib/X11/fonts/misc/fonts.dir

        

In this case, the mode, MD5 checksum, and modification time for the specified file have failed to verify. The file is not a config file (Note the absence of a "c" between the attribute list and the filename).


S.5....T c /etc/passwd

        

Here, the size, checksum, and modification time of the system password file have all changed. The "c" indicates that this is a config file.


missing    /var/spool/at/spool

        

This last example illustrates what RPM does when a file, that should be there, is missing entirely.

Other Verification Failure Messages

When rpm -V finds other problems, the output is a bit easier to understand:

# rpm -V blather

Unsatisfied dependencies for blather-7.9-1: bother >= 3.1

#
          

It's pretty easy to see that the blather package requires at least version 3.1 of the bother package.

The output from a package's verification script is a bit harder to categorize, as the script's contents, as well as its messages, are entirely up to the package builder.

Selecting What to Verify, and How

There are several ways to verify packages installed on your system. If you've taken a look at RPM's query command, you'll find that many of them are similar. Let's start with the simplest method of specifying packages — the package label.

The Package Label — Verify an Installed Package Against the RPM Database

You can simply follow the rpm -V command with all or part of a package label. As with every other RPM command that accepts package labels, you'll need to carefully specify each part of the label you include. Keep in mind that package names are case-sensitive, so rpm -V PackageName and rpm -V packagename are not the same. Let's verify the initscripts package:

# rpm -V initscripts
# 
          

While it looks like RPM didn't do anything, the following steps were performed:

  • For every file in the package, RPM checked the nine file attributes that were discussed above.

  • If the package was built with dependencies, the RPM database was searched to ensure the packages that satisfy those dependencies were installed.

  • If the package was built with a verification script, that script was executed.

In our example, each of these steps was performed without error — the package verified successfully. Remember, with rpm -V you'll only see output if a package fails to verify.

-a — Verify All Installed Packages Against the RPM Database

If you add -a to rpm -V, you can easily verify every installed package on your system. It might take a while, but when it's done, you'll know exactly what's been changed on your system:

# rpm -Va

.M5....T   /usr/X11R6/lib/X11/fonts/misc/fonts.dir
missing    /var/spool/at/.lockfile
missing    /var/spool/at/spool
S.5....T   /usr/lib/rhs/glint/icon.pyc
..5....T c /etc/inittab
..5.....   /usr/bin/loadkeys

#
          

Don't be too surprised if rpm -Va turns up a surprising number of files that failed verification. RPM's verification process is very strict! In many cases, the changes flagged don't indicate problems — they are only an indication of your system's configuration being different than what the builders of the installed packages had on their system. Also, some attributes change during normal system operation. However, it would be wise to check into each verification failure, just to make sure.

-f <file> — Verify the Package Owning <file> Against the RPM Database

Imagine this: you're hard at work when a program you've used a million times before suddenly stops working. What do you do? Well, before using RPM, you probably tried to find other files associated with that program and see if they had changed recently.

Now you can let RPM do at least part of that sleuthing for you. Simply direct RPM to verify the package owning the ailing program:

% rpm -Vf /sbin/cardmgr

S.5....T c /etc/sysconfig/pcmcia

% 
          

Hmmmm. Looks like a config file was recently changed.

This isn't to say that using RPM to verify a package will always get you out of trouble, but it's such a quick step it should be one of the first things you try. Here's an example of rpm -Vf not working out as well:

% rpm -Vf /etc/blunder

file /etc/blunder is not owned by any package

% 
          

(Note that the issue surrounding RPM and symbolic links mentioned in the section called “A Tricky Detail” also applies to rpm -Vf. Watch those symlinks!)

-p <file> — Verify Against a Specific Package File

Unlike the previous options to rpm -V, each of which verified one or more packages against RPM's database, the -p option performs the same verification, but against a package file. Why on earth would you want to do this when the RPM database is sitting there just waiting to be used?

Well, what if you didn't have an RPM database? While it isn't a common occurrence, power failures, hardware problems, and inadvertent deletions (along with non-existent backups) can leave your system "sans database". Then your system hiccups — what do you do now?

This is where a CD full of package files can be worth its weight in gold. Simply mount the CD and verify to your heart's content:

# rpm -Vp /mnt/cdrom/RedHat/RPMS/i386/adduser-1.1-1.i386.rpm
#
          

Whatever else might be wrong with this system, at least we can add new users. But what if you have many packages to verify? It would be a very slow process doing it one package at a time. That's where the next option comes in handy…

-g <group> — Verify Packages Belonging To <group>

When a package is built, the package builder must classify the package, grouping it with other packages that perform similar functions. RPM gives you the ability to verify installed packages based on their groups. For example, there is a group known as Shells. This group consists of packages that contain, strangely enough, shells. Let's verify the proper installation of every shell-related package on the system:

# rpm -Vg Shells

missing    /etc/bashrc

#
          

One thing to keep in mind is that group specifications are case-sensitive. Issuing the command rpm -Vg shells wouldn't verify many packages:

# rpm -Vg shells

group shells does not contain any packages

#
          

--nodeps: Do Not Check Dependencies During Verification

When the --nodeps option is added to a verify command, RPM will bypass its dependency verification processing. In this example, we've added the -vv option to so we can watch RPM at work:

# rpm -Vvv rpm

D: opening database in //var/lib/rpm/
D: verifying record number 2341208
D: dependencies: looking for libz.so.1
D: dependencies: looking for libdb.so.2
D: dependencies: looking for libc.so.5

#
          

As we can see, there are three different capabilities that the rpm package requires:

  • libz.so.1

  • libdb.so.2

  • libc.so.5

If we add the --nodeps option, the dependency verification of the three capabilities is no longer performed:

# rpm -Vvv --nodeps rpm

D: opening database in //var/lib/rpm/
D: verifying record number 2341208

#
          

The line D: verifying record number 2341208 indicates that RPM's normal file-based verification proceeded normally.

--noscripts: Do Not Execute Verification Script

Adding the --noscripts option to a verify command prevents execution of the verification scripts of each package being verified. In the following example, the package verification script is executed:

# rpm -Vvv bother

D: opening database in //var/lib/rpm/
D: verifying record number 616728
D: verify script found - running from file /var/tmp/rpm-321.vscript
+ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin
+ export PATH
+ echo This is the bother 3.5 verification script
This is the bother 3.5 verification script

#
          

While the actual script is not very interesting, it did execute when the package was being verified. In the next example, we'll use the --noscripts option to prevent its execution:

# rpm -Vvv --noscripts bother

D: opening database in //var/lib/rpm/
D: verifying record number 616728

#
          

As expected, the output is identical to the prior example — minus the lines dealing with the verification script, of course.

--nofiles: Do Not Verify File Attributes

The --nofiles option disables RPM's file-related verification processing. When this option is used, only the verification script and dependency verification processing are performed. In this example, the package has a file-related verification problem:

# rpm -Vvv bash

D: opening database in //var/lib/rpm/
D: verifying record number 279448
D: dependencies: looking for libc.so.5
D: dependencies: looking for libtermcap.so.2
missing    /etc/bashrc

#
          

When the --nofiles option is added, the missing file doesn't cause a message any more:

# rpm -Vvv --nofiles bash

D: opening database in //var/lib/rpm/
D: verifying record number 279448
D: dependencies: looking for libc.so.5
D: dependencies: looking for libtermcap.so.2

#
          

This is not to say that the missing file problem is solved, just that no file verification was performed.

-v — Display Additional Information

Although RPM won't report an error with the command syntax if you include the -v option, you won't see much in the way of additional output:

# rpm -Vv bash
#
          

Even if there are verification errors, adding -v won't change the output:

# rpm -Vv apmd

S.5....T   /etc/rc.d/init.d/apm
S.5....T   /usr/X11R6/bin/xapm

#
          

The only time that the -v option will produce output is when the package being verified has a verification script. Any normal output from the script won't be displayed by RPM, when run without -v: [32]

# rpm -V bother
#
          

But when -v is added, the script's non-error-related output is displayed:

# rpm -Vv bother

This is the bother 3.5 verification script

#
          

If you're looking for more insight into RPM's inner workings, you'll have to try the next option:

-vv — Display Debugging Information

Sometimes it's necessary to have even more information than we can get with -v. By adding another v, that's just what we'll get:

# rpm -Vvv rpm

D: opening database in //var/lib/rpm/
D: verifying record number 2341208
D: dependencies: looking for libz.so.1
D: dependencies: looking for libdb.so.2
D: dependencies: looking for libc.so.5

#
          

The lines starting with D: have been added by using -vv. We can see where the RPM database is located and what record number contains information on the rpm-2.3-1 package. Following that is the list of dependencies that the rpm package requires.

In the vast majority of cases, it will not be necessary to use -vv. It is normally used by software engineers working on RPM itself, and the output can change without notice. However, it's a handy way to gain insights into RPM.

--dbpath <path>: Use <path> To Find RPM Database

In order for RPM to do its handiwork, it needs access to an RPM database. Normally, this database exists in the directory specified by the rpmrc file entry, dbpath. By default, dbpath is set to /var/lib/rpm.

Although the dbpath entry can be modified in the appropriate rpmrc file, the --dbpath option is probably a better choice when the database path needs to be changed temporarily. An example of a time the --dbpath option would come in handy is when it's necessary to examine an RPM database copied from another system. Granted, it's not a common occurrence, but it's difficult to handle any other way.

--root <path>: Set Alternate Root to <path>

Adding --root <path> to a verify command forces RPM to assume that the directory specified by <path> is actually the "root" directory. In addition, RPM expects its database to reside in the directory specified by the dbpath rpmrc file entry, relative to <path>. [33]

Normally this option is only used during an initial system install, or when a system has been booted off a "rescue disk", and some packages need to be re-installed in order to restore normal operation.

--rcfile <rcfile>: Set Alternate rpmrc file to <rcfile>

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc, located in your login directory.

This option would be used if there was a need to switch between several sets of RPM options. Software developer and package builders will be the people using --rcfile. For more information on rpmrc files, see Appendix B, The rpmrc File.

We've Lied to You…

Not really; we just omitted a few details until you've had a chance to see rpm -V in action. Here are the details:

RPM Controls What Gets Verified

Depending on the type of file being verified, RPM will not verify every possible attribute. Here is a table showing the attributes checked for each of the different file types:

Table 6.2. Verification Versus File Types

File Type File Size Mode MD5 Checksum Major Number Minor Number Symlink String Owner Group Modification Time
Directory File - X - - - - X X -
Symbolic Links - X - - - X X X -
FIFO - X - - - - X X -
Devices - X - X X - X X -
Regular Files X X X - - - X X X

The Package Builder Can Also Control What Gets Verified

When a package builder creates a new package, they can control what attributes are to be verified on a file-by-file basis. The reasons for excluding specific attributes from verification can be quite involved, but here's an example just to give you the flavor:

When a person logs into a system, there are device files associated with that user's terminal session. In order for the terminal device (called tty) to function properly, the owner and group of the device must change to that of the person logging in. Therefore, if RPM were to verify the package that created the tty device files, any ttys that were in use at the time would fail to verify. However, by using the %verify [34] directive, a package builder can save you from trivial verification failures.



[30] Actually, the price is fairly low. For a completely RPM-based Linux distribution, it would be unusual to have a database over 5MB in size.

[31] From a strictly theoretical standpoint, this is not entirely true. Using the lingo of cryptologists, it is believed to be "computationally infeasible" to find two messages that produce the same MD5 checksum.

[32] Failure messages will always be displayed.

[33] For more information on rpmrc file entries, see Appendix B, The rpmrc File.

Chapter 7. Using RPM to Verify Package Files

Table 7.1. rpm -K Command Syntax

rpm -K (or --checksig) options file1.rpmfileN.rpm
Parameters
file1.rpmfileN.rpm One or more RPM package files (URLs OK)
Checksig-specific Options Page
--nopgp Do not verify PGP signatures the section called “ --nopgp — Do Not Verify Any PGP Signatures ”
General Options Page
-v Display additional information the section called “-v — Display Additional Information”
-vv Display debugging information the section called “-vv — Display Debugging Information”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File ”

rpm -K — What Does it Do?

One aspect of RPM is that you can get a package from the Internet, and easily install it. But what do you know about that package file? Is the organization listed as being the "vendor" of the package really the organization that built it? Did someone make unauthorized changes to it? Can you trust that, if installed, it won't mail a copy of your password file to a system cracker?

Features built into RPM allow you to make sure that the package file you've just gotten won't cause you problems once it's installed, whether the package was corrupted by line noise when you downloaded it, or something more sinister happened to it.

The command rpm -K (The option --checksig is equivalent) verifies a package file. Using this command, it is easy to make sure the file has not been changed in any way. rpm -K can also be used to make sure that the package was actually built by the organization listed as being the package's vendor. That's all very impressive, but how does it do that? Well, it just needs help from some "Pretty Good" software.

Pretty Good Privacy: RPM's Assistant

The "Pretty Good" software we're referring to is known as "Pretty Good Privacy", or PGP. While all the information on PGP could fill a book (or several), we've provided a quick introduction to help you get started.

If PGP is new to you, a quick glance through Appendix G, An Introduction to PGP should get you well on your way to understanding, building, and installing PGP. If, on the other hand, you've got PGP already installed and have sent an encrypted message or two, you're probably more than ready to continue with this chapter.

Configuring PGP for rpm -K

Once PGP is properly built and installed, the actual configuration for RPM is trivial. Here's what needs to be done:

  • PGP must be in your path. If PGP's usage message doesn't come up when you enter pgp at your shell prompt, you'll need to add PGP's directory to your path.

  • PGP must be able to find the public keyring file that you want to use when checking package file signatures. You can use two methods to direct PGP to the public keyring:

    1. Set the PGPPATH environment variable to point to the directory containing the public keyring file.

    2. Set the pgp_path rpmrc file entry to point to the directory containing the public keyring file. [35]

Now we're ready.

Using rpm -K

After all the preliminaries with PGP, it's time to get down to business. First, we need to get the package builder's public key and add it to the public keyring file used by RPM. You'll need to do this once for each package builder whose packages you'll want to check. This is what you'll need to do:

# pgp -ka RPM-PGP-KEY ./pubring.pgp

Pretty Good Privacy(tm) 2.6.3a - Public-key encryption for the masses.
(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software. 1996-03-04
Uses the RSAREF(tm) Toolkit, which is copyright RSA Data Security, Inc.
Distributed by the Massachusetts Institute of Technology.
Export of this software may be restricted by the U.S. government.
Current time: 1996/06/01 22:50 GMT

Looking for new keys...
pub  1024/CBA29BF9 1996/02/20  Red Hat Software, Inc. <redhat@redhat.com>

Checking signatures...

Keyfile contains:
   1 new key(s)

One or more of the new keys are not fully certified.

Do you want to certify any of these keys yourself (y/N)? n
        

Here we've added Red Hat's public key, since we're going to check some package files produced by them. The file RPM-PGP-KEY contains the key. At the end, PGP asks us if we want to certify the new key. We've answered "no" since it isn't necessary to certify keys to verify package files.

Next, we'll verify a package file:

# rpm -K rpm-2.3-1.i386.rpm

rpm-2.3-1.i386.rpm: size pgp md5 OK

#
        

While the output might seem somewhat anti-climactic, we can now be nearly 100% certain this package:

  1. was produced by Red Hat.

  2. is unchanged from their original copy.

The output from this command shows that there are actually three distinct features of the package file that are checked by the -K option:

  1. The size message indicates that the size of the packaged files has not changed.

  2. The pgp message indicates that the digital signature contained in the package file is a valid signature of the package file contents, and was produced by the organization that originally signed the package.

  3. The md5 message indicates that a checksum contained in the package file and calculated when the package was built, matches a checksum calculated by RPM during verification. Because the two checksums match, it is unlikely that the package has been modified.

The OK means that each of these tests were successful. If any had failed, the name would have been printed in parentheses. A bit later in the chapter, we'll see what happens when there are verification problems.

-v — Display Additional Information

Adding v to a verification command will produce more interesting output:

# rpm -Kv rpm-2.3-1.i386.rpm

rpm-2.3-1.i386.rpm:
Header+Archive size OK: 278686 bytes
Good signature from user "Red Hat Software, Inc. <redhat@redhat.com>".
Signature made 1996/12/24 18:37 GMT using 1024-bit key, key ID CBA29BF9

WARNING:  Because this public key is not certified with a trusted
signature, it is not known with high confidence that this public key
actually belongs to: "Red Hat Software, Inc. <redhat@redhat.com>".
MD5 sum OK: 8873682c5e036a307dee87d990e75349

#
          

With a bit of digging, we can see that each of the three tests was performed, and each passed. The reason for that dire-sounding warning is that PGP is meant to operate without a central authority managing key distribution. PGP certifies keys based on webs of trust. For example, if an acquaintance of yours creates a public key, you can certify it by attaching your digital signature to it. Then anyone that knows and trusts you can also trust your acquaintance's public key.

In this case, the key came directly from a mass-produced Red Hat Linux CDROM. If someone was trying to masquerade as Red Hat then they have certainly gone through a lot of trouble to do so. In this case, the lack of a certified public key is not a major problem, given the fact that the CDROM came directly from the Red Hat offices. [36]

When the Package is Not Signed

As mentioned earlier, not every package you'll run across is going to be signed. If this is the case, here's what you'll see from RPM:

# rpm -K bother-3.5-1.i386.rpm

bother-3.5-1.i386.rpm: size md5 OK

#
          

Note the lack of a pgp message. The size and md5 messages indicate that the package still has size and checksum information that verified properly. In fact, all recently-produced package files will have these verification measures built in automatically.

If you happen to run across an older unsigned package, you'll know it right away:

# rpm -K apmd-2.4-1.i386.rpm

apmd-2.4-1.i386.rpm: No signature available

#
          

Older package files had only a PGP-based signature; if that was missing, there was nothing left to verify.

When You Are Missing the Correct Public Key

If you happen to forget to add the right public key to RPM's keyring, you'll see the following response:

# rpm -K rpm-2.3-1.i386.rpm

rpm-2.3-1.i386.rpm: size (PGP) md5 OK (MISSING KEYS)

#
          

Here the PGP in parentheses indicates that there's a problem with the signature, and the message at the end of the line (MISSING KEYS) shows what the problem is. Basically, RPM asked PGP to verify the package against a key that PGP didn't have, and PGP complained.

When a Package Just Doesn't Verify

Eventually it's going to happen — you go to verify a package, and it fails. We'll look at an example of a package that fails verification a bit later. Before we do that, let's make a package that won't verify, to demonstrate how sensitive RPM's verification is.

First, we made a copy of a signed package, rpm-2.3-1.i386.rpm, to be specific. We called the copy rpm-2.3-1.i386-bogus.rpm. Next, using Emacs (in hexl-mode, for all you Emacs buffs), we changed the first letter of the name of the system that built the original package. The file rpm-2.3-1.i386-bogus.rpm is now truly bogus: it has been changed from the original file.

Although the change was a small one, it still showed up when the package file was queried. Here's a listing from the original package:

# rpm -qip rpm-2.3-1.i386.rpm

Name        : rpm                   Distribution: Red Hat Linux Vanderbilt
Version     : 2.3                         Vendor: Red Hat Software
Release     : 1                       Build Date: Tue Dec 24 09:07:59 1996
Install date: (none)                  Build Host: porky.redhat.com
Group       : Utilities/System        Source RPM: rpm-2.3-1.src.rpm
Size        : 631157
Summary     : Red Hat Package Manager
Description :
RPM is a powerful package manager, which can be used to build, install,
query, verify, update, and uninstall individual software packages. A
package consists of an archive of files, and package information,
including name, version, and description.

# 
          

And here's the same listing from the bogus package file:

# rpm -qip rpm-2.3-1.i386-bogus.rpm

Name        : rpm                   Distribution: Red Hat Linux Vanderbilt
Version     : 2.3                         Vendor: Red Hat Software
Release     : 1                       Build Date: Tue Dec 24 09:07:59 1996
Install date: (none)                  Build Host: qorky.redhat.com
Group       : Utilities/System        Source RPM: rpm-2.3-1.src.rpm
Size        : 631157
Summary     : Red Hat Package Manager
Description :
RPM is a powerful package manager, which can be used to build, install,
query, verify, update, and uninstall individual software packages. A
package consists of an archive of files, and package information,
including name, version, and description.

# 
          

Notice that the build host name changed from porky.redhat.com to qorky.redhat.com. Using the cmp utility to compare the two files, we find that the difference occurs at byte 1201, which changed from "p" (octal 160), to "q" (octal 161):

# cmp -cl rpm-2.3-1.i386.rpm rpm-2.3-1.i386-bogus.rpm

  1201 160 p    161 q

#
          

People versed in octal numbers will note that only one bit has been changed in the entire file. That's the smallest possible change you can make! Let's see how our bogus friend fares:

# rpm -K rpm-2.3-1.i386-bogus.rpm

rpm-2.3-1.i386-bogus.rpm: size PGP MD5 NOT OK

#
          

Given that the command's output ends with NOT OK in big capital letters, it's obvious there's a problem. Since the word size was printed in lowercase, the bogus package's size was OK, which makes sense — we only changed the value of one bit without adding or subtracting anything else.

However, the PGP signature, printed in uppercase, didn't verify. Again, this makes sense, too. The package that was signed by Red Hat has been changed. The fact that the package's MD5 checksum also failed to verify provides further evidence that the bogus package is just that: bogus.

--nopgp — Do Not Verify Any PGP Signatures

Perhaps you want to be able to verify packages but, for one reason or another, you cannot use PGP. Maybe you don't have a trustworthy source of the necessary public keys, or maybe it's illegal to possess encryption (like PGP) software in your country. Is it still possible to verify packages?

Certainly — in fact, we've already done it, in the section called “When You Are Missing the Correct Public Key”. You lose the ability to verify the package's origins, as well as some level of confidence in the package's integrity, but the size and MD5 checksums still give some measure of assurance as to the package's state.

Of course, when PGP can't be used, the output from a verification always looks like something's wrong:

# rpm -K rpm-2.3-1.i386.rpm

rpm-2.3-1.i386.rpm: size (PGP) md5 OK (MISSING KEYS)

#
          

The --nopgp option directs RPM to ignore PGP entirely. If we use the --nopgp option on our example above, we find that things look a whole lot better:

# rpm -K --nopgp rpm-2.3-1.i386.rpm

rpm-2.3-1.i386.rpm: size md5 OK

#
          

-vv — Display Debugging Information

Nine times out of ten, you'll probably never have to use it, but if you're the curious type, the -vv option will give you insights into how RPM verifies packages. Here's an example:

# rpm -Kvv rpm-2.3-1.i386.rpm

D: New Header signature
D: magic: 8e ad e8 01
D: got  : 8e ad e8 01
D: Signature size: 236
D: Signature pad : 4
D: sigsize         : 240
D: Header + Archive: 278686
D: expected size   : 278686
rpm-2.3-1.i386.rpm:
Header+Archive size OK: 278686 bytes
Good signature from user "Red Hat Software, Inc. <redhat@redhat.com>".
Signature made 1996/12/24 18:37 GMT using 1024-bit key, key ID CBA29BF9

WARNING:  Because this public key is not certified with a trusted
signature, it is not known with high confidence that this public key
actually belongs to: "Red Hat Software, Inc. <redhat@redhat.com>".
MD5 sum OK: 8873682c5e036a307dee87d990e75349

# 
          

The lines starting with D: represent extra output produced by the -vv option. This output is normally used by software developers in the course of adding new features to RPM and is subject to change, but there's no law against looking at it.

Briefly, the output shows that RPM has detected a new-style signature block, containing size, MD5 checksum, and PGP signature information. The size of the signature, the size of the package file's header and archive sections, and the expected size of those sections are all displayed.

--rcfile <rcfile>: Use <rcfile> As An Alternate rpmrc File

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc located in your login directory.

This option would be used if there was a need to switch between several sets of RPM defaults. Software developers and package builders will normally be the only people using the --rcfile option. For more information on rpmrc files, see Appendix B, The rpmrc File.



[35] For more information on rpmrc files, rpmrc file entries, and how to use them, please see Appendix B, The rpmrc File.

[36] Red Hat Software's public key is also available from their website, at http://www.redhat.com/redhat/contact.html . The RPM sources also contain the key, and are available from their FTP site at ftp://ftp.redhat.com/pub/redhat/code/rpm.

Chapter 8. Miscellanea

As with any other large, complex subject, there are always some leftovers — things that just don't seem to fit in any one category. RPM is no exception. This chapter covers those aspects of RPM that can only be called "miscellanea"…

Other RPM Options

The following options are not normally used on a day to day basis. However, some of them can be quite important when the need arises. One such option is --rebuilddb.

--rebuilddb — Rebuild RPM database

We all hope the day never comes, and for many of us, it never does. But still, there is a chance that one day, while you're busy using RPM to install or upgrade a package, you'll see this message:


free list corrupt (42)- contact rpm-list@redhat.com

          

Once this happens, you'll find there's very little that you can do, RPM-wise. However, before you fire off an e-mail to the RPM mailing list, you might try the --rebuilddb option. The format of the command is simple:


rpm --rebuilddb

          

The command produces no output, either. After a few minutes, it completes with nary a peep. Here's an example of --rebuilddb being used on an RPM database that wasn't corrupt. First, let's look at the files that comprise the database:

# cd /var/lib/rpm
# ls

total 3534
-rw-r--r--   1 root     root      1351680 Oct 17 10:35 fileindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 10:35 groupindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 10:35 nameindex.rpm
-rw-r--r--   1 root     root      2342536 Oct 17 10:35 packages.rpm
-rw-r--r--   1 root     root        16384 Oct 17 10:35 providesindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 10:35 requiredby.rpm

#
          

Then, we issue the command:

# rpm --rebuilddb
#
          

After a few minutes, the command completes, and we take a look at the files again:

# ls

total 3531
-rw-r--r--   1 root     root      1351680 Oct 17 20:50 fileindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 20:50 groupindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 20:50 nameindex.rpm
-rw-r--r--   1 root     root      2339080 Oct 17 20:50 packages.rpm
-rw-r--r--   1 root     root        16384 Oct 17 20:50 providesindex.rpm
-rw-r--r--   1 root     root        16384 Oct 17 20:50 requiredby.rpm

# 
          

You'll note that packages.rpm decreased in size. This is due to a side-effect of the --rebuilddb option — While it is going through the database, it is getting rid of unused portions of the database. Our example was performed on a newly installed system where only one or two packages had been upgraded, so the reduction in size was small. For a system that has been through a complete upgrade, the difference would be more dramatic.

Does this mean that you should rebuild the database every once in a while? Not really. Since RPM eventually will make use of the holes, there's no major advantage to regular rebuilds. However, when an RPM-based system has undergone a major upgrade, it certainly wouldn't hurt to spend a few minutes using --rebuilddb to clean things up.

--initdb — Create a New RPM Database

If you are already using RPM, the --initdb option is one you'll probably never have to use. The --initdb option is used to create a new RPM database. That's why you'll probably not need it if you're already using RPM — you already have an RPM database.

It might seem that the --initdb option would be dangerous. After all, won't it trash your current database if you mistakenly use it? Fortunately, the answer is no. If there is an RPM database in place already, it's still perfectly safe to use the option, even though it won't accomplish much. As an example, here's a listing of the files that make up the RPM database on a Red Hat Linux system:

# ls /var/lib/rpm

total 3559
-rw-r--r--   1 root     root        16384 Jan  8 22:10 conflictsindex.rpm
-rw-r--r--   1 root     root      1351680 Jan  8 22:10 fileindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 groupindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 nameindex.rpm
-rw-r--r--   1 root     root      2349640 Jan  8 22:10 packages.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 providesindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 requiredby.rpm

#
          

Next, let's use the --initdb option, just to see what it does to this database:

# rpm --initdb
# ls /var/lib/rpm

total 3559
-rw-r--r--   1 root     root        16384 Jan  8 22:10 conflictsindex.rpm
-rw-r--r--   1 root     root      1351680 Jan  8 22:10 fileindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 groupindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 nameindex.rpm
-rw-r--r--   1 root     root      2349640 Jan  8 22:10 packages.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 providesindex.rpm
-rw-r--r--   1 root     root        16384 Jan  8 22:10 requiredby.rpm

# 
          

Since an RPM database existed already, the --initdb option did no harm to it — there was no change to the database files.

The only other option that can be used with --initdb is --dbpath. This permits the easy creation of a new RPM database in the directory specified with the --dbpath option.

--quiet — Produce as little output as possible

Adding the --quiet option to any RPM command directs RPM to produce as little output as possible. For example, RPM's build command (the subject of the second half of this book) normally produces reams of output; by adding the --quiet option, this is all you'll see:

# rpmbuild -ba --quiet bother-3.5.spec

* Package: bother
1 block
3 blocks

#
          

The --quiet option can silence even the mighty -vv option:

# rpm -Uvv --quiet eject-1.2-2.i386.rpm
#
          

--help — Display a help message

RPM includes a concise built-in help message for those times when you need a reminder about a particular command. Normally you'll want to use the --help option by itself, though you might want to pipe the output through a pager such as less, since the output is more than one screen long:

# rpm --help|less

RPM version 2.3
Copyright (C) 1995 - Red Hat Software
This may be freely redistributed under the terms of the GNU Public License

usage:
   --help               - print this message
   --version    - print the version of rpm being used
   all modes support the following arguments:
      --rcfile <file>   - use <file> instead of /etc/rpmrc and $HOME/.rpmrc
       -v                     - be a little more verbose
       -vv                    - be incredibly verbose (for debugging)
   -q                   - query mode
      --root <dir>        - use <dir> as the top level directory
      --dbpath <dir>      - use <dir> as the directory for the database
      --queryformat <s>   - use s as the header format (implies -i)
   install, upgrade and query (with -p) allow ftp URL's to be used in place
   of file names as well as the following options:

      --ftpproxy <host>   - hostname or IP of ftp proxy

      --ftpport <port>    - port number of ftp server (or proxy)

          

This is just the first screen of RPM's help command. To see the rest, give the command a try. Practically everything there is to know about RPM is present in the --help output. It's a bit too concise to learn RPM from, but it's enough to refresh your memory when the syntax of a particular option escapes you.

--version — Display the current RPM version

If you're not sure what version of RPM is presently installed on your system, the easiest way to find out is to ask RPM itself using the --version option:

# rpm --version

RPM version 2.3

#
          

Using rpm2cpio

From time to time, you might find it necessary to extract one or more files from a package file. One way to do this would be to:

  • Install the package

  • Make a copy of the file(s) you need

  • Erase the package

An easier way would be to use rpm2cpio.

rpm2cpio — What does it do?

As the name implies, rpm2cpio takes an RPM package file and converts it to a cpio archive. Because it's written to be used primarily as a filter, there's not much to be specified. rpm2cpio takes only only one argument, and even that's optional!

The optional argument is the name of the package file to be converted. If there is no filename specified on the command line, rpm2cpio will simply read from standard input and convert that to a cpio archive. Let's give it a try:

# rpm2cpio logrotate-1.0-1.i386.rpm

0707020001a86a000081a4000000000000000000000001313118bb000002c200000008000
000030000000000000000000000190000e73eusr/man/man8/logrotate.8." logrotate
 - log fi
le rotator
.TH rpm 8 "28 November 1995" "Red Hat Software" "Red Hat Linux"
.SH NAME

          

(We've just shown the first few lines of output.)

What on earth is all that stuff? Remember, rpm2cpio is written as a filter. It writes the cpio archive contained in the package file to standard output, which, if you've not redirected it somehow, is your screen. Here's a more reasonable example:

# rpm2cpio logrotate-1.0-1.i386.rpm > blah.cpio
# file blah.cpio

blah.cpio: ASCII cpio archive (SVR4 with CRC)

#
          

Here we've directed rpm2cpio to convert the logrotate package file. We've also redirected rpm2cpio's output to a file called blah.cpio. Next, using the file command, we find that the resulting file is indeed a true-blue cpio archive file. The following command is entirely equivalent to the one above and shows rpm2cpio's ability to read the package file from its standard input:

# cat logrotate-1.0-1.i386.rpm | rpm2cpio > blah.cpio
#
          

A more real-world example — Listing the files in a package file

While there's nothing wrong with using rpm2cpio to actually create a cpio archive file, it's takes a few more steps and uses a bit more disk space than is strictly necessary. A somewhat cleaner approach would be to pipe rpm2cpio's output directly into cpio:

# rpm2cpio logrotate-1.0-1.i386.rpm  | cpio -t

usr/man/man8/logrotate.8
usr/sbin/logrotate
14 blocks

#
          

In this example, we used the -t option to direct cpio to produce a "table of contents" of the archive created by rpm2cpio. This can make it much easier to get the right filename and path when you want to extract a file.

Extracting one or more files from a package file

Continuing the example above, let's extract the man page from the logrotate package. In the table of contents, we see that the full path is usr/man/man8/logrotate.8. All we need to do is to use the filename and path as shown below:

# rpm2cpio logrotate-1.0-1.i386.rpm |cpio -ivd usr/man/man8/logrotate.8

usr/man/man8/logrotate.8
14 blocks

#
          

In this case, the cpio options -i, -v, and -d direct cpio to:

  • Extract one or more files from an archive.

  • Display the names of any files processed, along with the size of the archive file, in 512-byte blocks. [37]

  • Create any directories that precede the filename specified in the cpio command.

So where did the file end up? The last option (-d) to cpio provides a hint. Let's take a look:

# ls -al

total 5
-rw-rw-r--   1 root     root         3918 May 30 11:02 logrotate-1.0-1.i386.rpm
drwx------   3 root     root         1024 Jul 14 12:42 usr

# cd usr
# ls -al

total 1
drwx------   3 root     root         1024 Jul 14 12:42 man

# cd man
# ls -al

total 1
drwx------   2 root     root         1024 Jul 14 12:42 man8

# cd man8
# ls -al

total 1
-rw-r--r--   1 root     root          706 Jul 14 12:42 logrotate.8

# cat logrotate.8

.\" logrotate - log file rotator
.TH rpm 8 "28 November 1995" "Red Hat Software" "Red Hat Linux"
.SH NAME
logrotate \- log file rotator
.SH SYNOPSIS
\fBlogrotate\fP [configfiles] 
.SH DESCRIPTION
\fBlogrotate\fP is a tool to prevent log files from growing without
…

#
          

Since the current directory didn't have a usr/man/man8/ path in it, the -d option caused cpio to create all the directories leading up to the logrotate.8 file in the current directory. Based on this, it's probably safest to use cpio outside the normal system directories unless you're comfortable with cpio, and you know what you're doing!

Source Package Files and How To Use Them

One day, you may run across a package file with a name similar to the following:

etcskel-1.0-3.src.rpm
        

Notice the src. Is that a new kind of computer? If you use RPM on an Intel-based computer, you'd normally expect to find i386 there. Maybe someone messed up the name of the file. Well, we know that the file command can display information about a package file, even if the filename has been changed. We've used it before to figure out what package a file contains:

# file foo.bar

foo.bar: RPM v2 bin i386 eject-1.2-2

#
        

In this example, foo.bar is an RPM version 2 file, containing an executable package — hence, the "bin" — built for Intel processors — the "i386". The package is eject version 1.2, release 2.

Let's try the file command on this mystery file and see what we can find out about it:

# file etcskel-1.0-3.src.rpm

etcskel-1.0-3.src.rpm: RPM v2 src i386 etcskel-1.0-3

#
        

Well, it's a package file all right — for version 1.0, release 3 of the etcskel package. It's in RPM version 2 format, and built for Intel-based systems. But what does the "src" mean?

A gentle introduction to source code

This package file contains not the executable, or "binary", files that a normal package contains, but rather the "source" files required to create those binaries. When programmers create a new program, they write the instructions, often called "code", in one or more files. The source code is then compiled into a binary that can be executed by the computer.

As part of the process of building package files (a process we cover in great detail in the second half of this book), two types of package files are created:

  1. The binary, or executable, package file

  2. The source package file

The source package contains everything needed to recreate not only the programs and associated files that are contained in the binary package file, but the binary and source package files themselves.

Do you really need more information than this?

The following discussion is going to get rather technical. Unless you're the type of person who likes to take other people's code and modify it, chances are you won't need much more information than this. But if you're still interested, let's explore further.

So what can I do with it?

In the case of source package files, one of the things that can be done with them is that they can be installed. Let's try an install of a source package:

# rpm -i cdp-0.33-3.src.rpm
#
          

Well that doesn't tell us very much and, take our word for it, adding -v doesn't improve the situation appreciably. Let's haul out the big guns and try -vv:

# rpm -ivv cdp-0.33-3.src.rpm

D: installing cdp-0.33-3.src.rpm
Installing cdp-0.33-3.src.rpm
D: package is a source package major = 2
D: installing a source package
D: sources in: ///usr/src/redhat/SOURCES
D: spec file in: ///usr/src/redhat/SPECS
D: file "cdp-0.33-cdplay.patch" complete
D: file "cdp-0.33-fsstnd.patch" complete
D: file "cdp-0.33.spec" complete
D: file "cdp-0.33.tgz" complete
D: renaming ///usr/src/redhat/SOURCES/cdp-0.33.spec to ///usr/src/redhat/SPECS/cdp-0.33.spec

#
          

What does this output tell us? Well, RPM recognizes that the file is a source package. It mentions that sources (we know what they are) are in /usr/src/redhat/SOURCES. Let's take a look:

# ls -al /usr/src/redhat/SOURCES/

-rw-rw-r--   1 root     root          364 Apr 24 22:35 cdp-0.33-cdplay.patch
-rw-r--r--   1 root     root          916 Jan  8 12:07 cdp-0.33-fsstnd.patch
-rw-r--r--   1 root     root       148916 Nov 10  1995 cdp-0.33.tgz

#
          

There are some files that seem to be related to cdp there. The two files ending with ".patch" are patches to the source. RPM permits patches to be processed when building binary packages. The patches are bundled along with the original, unmodified sources in the source package.

The last file is a gzipped tar file. If you've gotten software off the Internet, you're probably familiar with tar files, gzipped or not. If we look inside the file, we should see all the usual kinds of things: README files, a Makefile or two, and some source code:

# tar ztf cdp-0.33.tgz

cdp-0.33/COPYING
cdp-0.33/ChangeLog
cdp-0.33/INSTALL
cdp-0.33/Makefile
cdp-0.33/README
cdp-0.33/cdp
cdp-0.33/cdp-0.33.lsm
cdp-0.33/cdp.1
cdp-0.33/cdp.1.Z
cdp-0.33/cdp.c
cdp-0.33/cdp.h

#
          

There's more, but you get the idea. OK, so there are the sources. But what is that "spec" file mentioned in the output? It mentions something about "/usr/src/redhat/SPECS", so let's see what we have in that directory:

# ls -al /usr/src/redhat/SPECS

-rw-r--r--   1 root     root          397 Apr 24 22:36 cdp-0.33.spec

          

Without making a long story too short, a spec file contains information used by RPM to create the binary and source packages. Using the spec file, RPM:

  • Unpacks the sources.

  • Applies patches (if any exist).

  • Builds the software.

  • Creates the binary package file.

  • Creates the source package file.

  • Cleans up after itself.

The neatest part of this is that RPM does this all automatically, under the control of the spec file. That's about all we're going to say about how RPM builds packages. For more information, please refer to the second half of this book.

Stick with us!

As we've noted several times, we'll be covering the entire subject of building packages with RPM, in the second half of the book. Be forewarned, however: Package building, while straightforward, is not a task for people new to programming. But if you've written a program or two, you'll probably find RPM's package building a piece of cake.



[37] Note that the size displayed by cpio is the size of the cpio archive and not the package file.

RPM and Developers — How to Distribute Your Software More Easily With RPM

Table of Contents

9. The Philosophy Behind RPM
Pristine Sources
Easy Builds
Reproducible Builds
Unattended Builds
Multi-architecture/operating system Support
Easier For Your Users
Easy Upgrades
Intelligent Configuration File Handling
Powerful Query Capabilities
Easy Package Verification
To Summarize…
10. The Basics of Developing With RPM
The Inputs
The Sources
The Patches
The Spec File
The Engine: RPM
The Outputs
The Source Package File
The Binary RPM
And Now…
11. Building Packages: A Simple Example
Creating the Build Directory Structure
Getting the Sources
Creating the Spec File
The Preamble
The %prep Section
The %build Section
The %install Section
The %files Section
The Missing Spec File Sections
Starting the Build
When Things Go Wrong
Problems During the Build
Testing Newly Built Packages
12. rpmbuild Command Reference
rpmbuild — What Does it Do?
rpmbuild -bp — Execute %prep
rpmbuild -bc — Execute %prep, %build
rpmbuild -bi — Execute %prep, %build, %install, %check
rpmbuild -bb — Execute %prep, %build, %install, %check, package (bin)
rpmbuild -ba — Execute %prep, %build, %install, %check, package (bin, src)
rpmbuild -bl — Check %files list
--short-circuit — Force build to start at particular stage
--buildarch <arch> — Perform Build For the <arch> Architecture
--buildos <os> — Perform Build For the <os> Operating System
--sign — Add a Digital Signature to the Package
--test — Create, Save Build Scripts For Review
--clean — Clean up after build
--buildroot <path> — Execute %install using <path> as the root
--timecheck <secs> — Print a warning if files to be packaged are over <secs> old
-vv — Display debugging information
--quiet — Produce as Little Output as Possible
--rcfile <rcfile> — Set alternate rpmrc file to <rcfile>
Other Build-related Commands
rpmbuild --recompile — What Does it Do?
rpmbuild --rebuild — What Does it Do?
13. Inside the Spec File
Comments: Notes Ignored by RPM
Tags: Data Definitions
Package Naming Tags
Descriptive Tags
Dependency Tags
Architecture- and Operating System-Specific Tags
Directory-related Tags
Source and Patch Tags
Scripts: RPM's Workhorse
Build-time Scripts
Install/Erase-time Scripts
Verification-Time Script — The %verifyscript Script
Macros: Helpful Shorthand for Package Builders
The %setup Macro
The %patch Macro
The %files List
Directives For the %files list
File-related Directives
Directory-related Directives
The Lone Directive: %package
-n <string> — Use <string> As the Entire Subpackage Name
Conditionals
The %ifarch Conditional
The %ifnarch Conditional
The %ifos Conditional
The %ifnos Conditional
The %else Conditional
The %endif Conditional
14. Adding Dependency Information to a Package
An Overview of Dependencies
Automatic Dependencies
The Automatic Dependency Scripts
Automatic Dependencies: An Example
The autoreqprov, autoreq, and autoprov Tags — Disable Automatic Dependency Processing
Manual Dependencies
The Requires Tag
The Conflicts Tag
The Provides Tag
To Summarize…
15. Making a Relocatable Package
Why relocatable packages?
The prefix tag: Relocation Central
Relocatable Wrinkles: Things to Consider
%files List Restrictions
Relocatable Packages Must Contain Relocatable Software
The Relocatable Software Is Referenced By Other Software
Building a Relocatable Package
Tying Up the Loose Ends
Test-Driving a Relocatable Package
16. Making a Package That Can Build Anywhere
Using Build Roots in a Package
Some Things to Consider
Having RPM Use a Different Build Area
Setting up a Build Area
Directing RPM to Use the New Build Area
Performing a Build in a New Build Area
Specifying File Attributes
%attr — How Does It Work?
Betcha Thought We Forgot…
17. Adding PGP Signatures to a Package
Why Sign a Package?
Getting Ready to Sign
Preparing PGP: Creating a Key Pair
Preparing RPM
Signing Packages
--sign — Sign a Package At Build-Time
--resign — Replace a Package's Signature(s)
--addsign — Add a Signature To a Package
18. Creating Subpackages
What Are Subpackages?
Why Are They Needed?
Our Example Spec File: Subpackages Galore!
Spec File Changes For Subpackages
The Subpackage's "Preamble"
The %files List
Install- and Erase-time Scripts
Build-Time Scripts: Unchanged For Subpackages
Our Spec File: One Last Look…
Building Subpackages
Giving Subpackages the Once-Over
19. Building Packages for Multiple Architectures and Operating Systems
Architectures and Operating Systems: A Primer
Let's Just Call Them Platforms
What Does RPM Do To Make Multi-Platform Packaging Easier?
Automatic Detection of Build Platform
Automatic Detection of Install Platform
Platform-Dependent Tags
Platform-Dependent Conditionals
Build and Install Platform Detection
Platform-Specific rpmrc Entries
Overriding Platform Information At Build-Time
Overriding Platform Information At Install-Time
optflags — The Other rpmrc File Entry
Platform-Dependent Tags
The excludexxx Tag
The exclusivexxx Tag
Platform-Dependent Conditionals
Common Features of All Conditionals
%ifxxx
%ifnxxx
Hints and Kinks
20. Real-World Package Building
An Overview of Amanda
Initial Building Without RPM
Setting Up A Test Build Area
Getting Software to build
Installing and testing
Initial Building With RPM
Generating patches
Making a first-cut spec file
Getting the original sources unpacked
Getting patches properly applied
Letting RPM do the Building
Letting RPM do the Installing
Testing RPM's Handiwork
Package Building
Creating the %files list
Testing those first packages
Finishing Touches
21. A Guide to the RPM Library API
An Overview of rpmlib
rpmlib Functions
Error Handling
Getting Package Information
Variable Manipulation
rpmrc-Related Information
RPM Database Manipulation
RPM Database Traversal
RPM Database Search
Package Manipulation
Package And File Verification
Dependency-Related Operations
Diagnostic Output Control
Signature Verification
Header Manipulation
Header Entry Manipulation
Header Iterator Support
Example Code
Example #1
Example #2
Example #3

Chapter 9. The Philosophy Behind RPM

As we saw in the first half of this book, RPM can make life much easier for the user. With automated installs, upgrades, and erasures, RPM can take a lot of the guesswork out of keeping a computer system up-to-date.

But what about people that sling code for a living? Does RPM have anything to offer them? The answer is yes! One of the best things about RPM is that although it was designed to make life easier for users, it was written by people that would be using it to build many packages. So the design philosophy of RPM has a definite bias toward making life easier for developers. Here are some of the reasons you should consider building packages with RPM:

Pristine Sources

While many developers might use RPM to package their own software, just as many, if not more, are going to be packaging software that they have not written. Because of this, there are some aspects to RPM's design that are geared toward "third-party" package builders. One such aspect is RPM's use of "pristine" sources.

When a third-party package builder decides to package someone else's software, they often get the software from the Net, normally as a tar file compressed with something like GNU zip. That's probably about the only generalization we can make when talking about software that is eligible for packaging. Once we look inside the tar file, there are a world of possible differences:

  • The application could be available in pure source form, in pure binary form, or some combination of both.

  • The application might have been written to be built using make, imake, or a script included with the sources. Or, it might have to be built entirely by hand.

  • The application might need to be configured prior to use. Maybe it uses GNU configure, a custom configuration script, or one or more files that need to be edited to reflect the target environment.

  • The application might have been written to reside in specific directories, and those directories do not exist, or are not appropriate on the target system.

  • The application might not even support the target environment, requiring all manner of changes to port it to the target environment.

We could go on, but you probably get the idea. It's a rare application that comes off the Net ready to package, and the changes required vary widely. What to do?

This is where the concept of pristine sources comes in. RPM has been designed to use the sources as they come from the application's developer, no matter how it has been packaged and configured. The main benefit is that the changes you as a package builder need to make, remain separate from the original sources, in a distinct collection of patches.

This may not sound like much of an advantage, but consider how this would work if a new version of the application came out. If the new version had a few localized bug fixes, it's entirely possible the original patches could be applied, and a new package built, with a single RPM command. Even if the patches didn't apply cleanly, it would at least give an indication as to what might need to be done to get the new version built and packaged.

If your users sometimes customize packages, having pristine sources makes it easier for them, too. They can see what patches you've created and can easily add their own.

Another benefit to using pristine sources is that it makes keeping track of multiple versions of a package simple. Instead of keeping patched sources around, or battling a revision control system, it's only necessary to keep:

  • The original sources in their tar file.

  • A copy of the patches you applied to get the application to build.

  • A file used by RPM to control the package building process.

With these three items, it's possible to easily build the package at any time. Keeping track of multiple versions only entails keeping track of each version of these three components, rather than hundreds or thousands of patched source files.

In fact, it gets better than that. RPM can also build a source package containing these three components. The source package, named using RPM's standard naming convention, keeps everything you need to recreate a specific version of a package, in one uniquely named file. Keeping track of multiple versions of multiple packages is simply a matter of keeping the appropriate source packages around. Everything else can be built from them.

Easy Builds

RPM makes it easy to build packages. Just as with the use of pristine sources, the fact that the build process is simple is an even greater advantage to the third-party package builders responsible for many packages, than it is to a one-package software development house. But in either case, RPM's ease of building is a welcome relief. The following sections document some of the ways that RPM makes building packages a straightforward process.

Reproducible Builds

One of the biggest problems facing developers is reproducing a particular build. This single problem is the main reason so much effort is put into creating and deploying version control systems to manage sources.

While RPM cannot compete with a full-blown revision control system, it does an excellent job of keeping in one place everything required to build a particular version of a package. Remember the source package we mentioned above? With one command, RPM can open the package, extract the sources, patch them, perform a build, and create a new binary package, ready for your users. The best part is that the binary package will be the same every time you build it because everything needed to create it is kept in one source package.

Unattended Builds

As we mentioned above, completely building a package takes only one RPM command. This makes it easy to set up automated build procedures that can build one hundred packages as easily as one. Anything from a single package consisting of one application to the several hundred packages that comprise an entire operating system, can be built automatically using RPM.

Multi-architecture/operating system Support

It has always been a fact of life for software developers that their applications may need to be ported to multiple operating systems. It is also becoming more common that a particular operating system might run on several different platforms, or architectures.

RPM's ability to support multiple architectures and operating systems makes it easy to build the same package for many OS/platform combinations. A package may be configured to build on only one architecture/OS combination, or on several. The only limitation is the application's portability.

Easier For Your Users

While we are primarily concerned with RPM's advantages from the developer's point of view, it's worth looking at RPM from the user's standpoint for a moment. After all, if RPM makes life easier for your users, that can translate into lower support costs.

Easy Upgrades

Probably the biggest headache for user and developer alike is the upgrade of an application, or worse yet, an entire operating system! RPM can make upgrading a one-step process. With one command, a new package can be installed, and the remnants of the old package removed.

Intelligent Configuration File Handling

Configuration files — nearly every application has them. They may go by different names, but they all control the behavior of their application. Users normally customize config files to their liking and would be upset if their customizations were lost during the installation, upgrade, or removal of a package.

RPM takes special care with a user's config files. By using MD5 checksums, RPM can determine what action is most appropriate with a config file. If a config file has been modified by the user and has to be replaced, it is saved. That way a user's modifications are never lost.

Powerful Query Capabilities

RPM uses a database to keep track of all files it installs. RPM's database provides other benefits, such as the wide variety of information that can be easily retrieved from it. RPM's query command makes it easy for users to quickly answer a number of questions, such as:

  • Where did this file come from? Is it part of a package?

  • What does this package do?

  • What packages are installed on my system?

These are just a few examples of the many ways RPM can provide information about one or more packages on a user's system.

Easy Package Verification

Another way that RPM leverages the information stored in its database, is by providing an easy way to verify that a package is properly installed. With this capability, RPM makes it easy to determine, for example, what packages were damaged by a wildcard delete in /usr/bin. In addition, RPM's verification command can detect changes to file attributes, such as a file's permissions, ownership, and size.

To Summarize…

RPM was written by developers for developers. It makes building packages as easy as possible, even if the software being packaged hasn't been developed in-house. In addition, RPM presents some significant advantages to users, thereby reducing support needs.

In the next chapter, we'll introduce the basic concepts of package building with RPM.

Chapter 10. The Basics of Developing With RPM

Now that we've seen the design philosophy of RPM, let's look at the nuts and bolts of RPM's build process. Building a package is similar to compiling code — there are inputs, an engine that does the dirty work, and outputs.

The Inputs

There are three different kinds of inputs that are used to drive RPM's build process. Two of the three inputs are required, and the third, strictly speaking, is optional. But unless you're packaging your own code, chances are you'll need it.

The Sources

First and foremost, are the sources. After all, without them, there wouldn't be much to build! In the case of packaging someone else's software, the sources should be kept as the author distributed them, which usually means a compressed tar file. RPM can handle other archive formats, but a bit more up-front effort is required.

In any case, you should not modify the sources used in the package building process. If you're a third-party package builder, that means the sources should be just the way you got them from the author's FTP site. If it's your own software, the choice is up to you, but you should consider starting with your mainstream sources.

The Patches

Why all the emphasis on unmodified sources? Because RPM gives you the ability to automatically apply patches to them. Usually, the nature of these patches falls into one of the following categories:

  • The patch addresses an issue specific to the target system. This could include changing makefiles to install the application into the appropriate directories, or resolving cross-platform conflicts, such as replacing BSD system calls with their SYSV counterparts.

  • The patch creates files that are normally created during a configuration step in the installation process. Many times, it's necessary to either edit configuration files or scripts in order to set things up for compilation. In other cases, a configuration utility needs to be run before the sources are compiled. In either instance, the patches create the environment required for proper compilation.

Creating the Patches

While it might sound a bit daunting to take into account the types of patches outlined above, it's really quite simple. Here's how it's done:

  1. Unpack the sources.

  2. Rename the top-level directory. Make it end with ".orig", for example.

  3. Unpack the sources again, leaving the top-level directory name unchanged.

The source tree that you created the second time will be the one you'll use to get the software to build.

If the software builds with no changes required, that's great — you won't need a patch. But if you had to make any changes, you'll have to create a set of patches. To do so, simply clean the source directory of any binaries. Then, issue a recursive diff command to compare the source tree you used for the build, against the original, unmodified source tree. It's as easy as that!

The Spec File

The spec file is at the heart of RPM's packaging building process. Similar in concept to a makefile, it contains information required by RPM to build the package, as well as instructions telling RPM how to build it. The spec file also dictates exactly what files are a part of the package, and where they should be installed.

As you might imagine, with this many responsibilities, the spec file format can be a bit complex. However, it's broken into several sections, making it easier to handle. All told, there are eight sections:

The Preamble

The preamble contains information that will be displayed when users request information about the package. This would include a description of the package's function, the version number of the software, and so on. Also contained in the preamble are lines identifying sources, patches, and even an icon to be used if the package is manipulated by graphical interface.

The Prep Section

The prep section is where the actual work of building a package starts. As the name implies, this section is where the necessary preparations are made prior to the actual building of the software. In general, if anything needs to be done to the sources prior to building the software, it needs to happen in the prep section. Usually, this boils down to unpacking the sources.

The contents of this section are an ordinary shell script. However, RPM does provide two macros to make life easier. One macro can unpack a compressed tar file and cd into the source directory. The other macro easily applies patches to the unpacked sources.

The Build Section

Like the prep section, the build section consists of a shell script. As you might guess, this section is used to perform whatever commands are required to actually compile the sources. This section could consist of a single make command, or be more complex if the build process requires it. Since most software is built today using make, there are no macros available in this section.

The Install Section

Also containing a shell script, the install section is used to perform the commands required to actually install the software. If the software's author added an install target in the makefile, this section might only consist of a make install command. Otherwise, you'll need to add the usual assortment of cp, mv, or install commands to get the job done.

Install and Uninstall Scripts

While the previous sections contained either information required by RPM to build the package, or the actual commands to do the deed, this section is different. It consists of scripts that will be run, on the user's system, when the package is actually installed or removed. RPM can execute a script:

  • Prior to the package being installed.

  • After the package has been installed.

  • Prior to the package being erased.

  • After the package has been erased.

One example of when this capability would be required is when a package contains shared libraries. In this case, ldconfig would need to be run after the package is installed or erased. As another example, if a package contains a shell, the file /etc/shells would need to be updated appropriately when the package was installed or erased.

The Verify Script

This is another script that is executed on the user's system. It is executed when RPM verifies the package's proper installation. While RPM does most of the work verifying packages, this script can be used to verify aspects of the package that are beyond RPM's capabilities.

The Clean Section

Another script that can be present is a script that can clean things up after the build. This script is rarely used, since RPM normally does a good job of clean-up in most build environments.

The File List

The last section consists of a list of files that will comprise the package. Additionally, a number of macros can be used to control file attributes when installed, as well as to denote which files are documentation, and which contain configuration information. The file list is very important — if it is missing, no package will be built.

The Engine: RPM

At the center of the action is RPM. It performs a number of steps during the build process:

  • Executes the commands and macros in the prep section of the spec file.

  • Checks the contents of the file list.

  • Executes the commands and macros in the build section of the spec file.

  • Executes the commands and macros in the install section of the spec file. Any macros in the file list are executed at this time, too.

  • Creates the binary package file.

  • Creates the source package file.

By using different options on the RPM command line, the build process can be stopped at any of the steps above. This makes the initial building of a package that much easier, as it is then possible to see whether each step completed successfully before continuing on to the next step.

The Outputs

The end product of this entire process is a source package file and a binary package file.

The Source Package File

The source package file is a specially formatted archive that contains the following files:

  • The original compressed tar file(s).

  • The spec file.

  • The patches.

Since the source package contains everything needed to create the binary package, the source package, and provide the original sources, it's a great way to distribute source code. As mentioned earlier, it's also a great way to archive all the information needed to rebuild a particular version of the package.

The Binary RPM

The binary package file is the one part of the entire RPM building process that is most visible to the user. It contains the files that comprise the application, along with any additional information needed to install and erase it. The binary package file is where the "rubber hits the road."

And Now…

Now that we've seen, in broad brush terms, the way RPM builds packages, let's take a look at an actual build. The next chapter will do just that, showing how simple it can be to build a package.

Chapter 11. Building Packages: A Simple Example

In the previous chapter, we looked at RPM's build process from a conceptual level. In this chapter, we will be performing an actual build using RPM. In order to keep things understandable for this first pass, the build will be very simple. Once we've covered the basics, we'll present more real-world examples in later chapters.

Creating the Build Directory Structure

RPM requires a set of directories in which to perform the build. While the directories' locations and names can be changed, unless there's a reason to do so, it's best to use the default layout. Note that if you've installed RPM, the build directories are most likely in place already.

The normal directory layout consists of a single top-level directory (The default name is /usr/src/redhat), with five subdirectories. The five subdirectories and their functions are:

  • /usr/src/redhat/SOURCES — Contains the original sources, patches, and icon files.

  • /usr/src/redhat/SPECS — Contains the spec files used to control the build process.

  • /usr/src/redhat/BUILD — The directory in which the sources are unpacked, and the software is built.

  • /usr/src/redhat/RPMS — Contains the binary package files created by the build process.

  • /usr/src/redhat/SRPMS — Contains the source package files created by the build process.

In general, there are no special requirements that need to be met when creating these directories. In fact, the only important requirement is that the BUILD directory be part of a filesystem with sufficient free space to build the largest package expected. Here is a directory listing showing a typical build directory tree:

# ls -lF /usr/src/redhat

total 5
drwxr-xr-x   3 root     root         1024 Aug  5 13:12 BUILD/
drwxr-xr-x   3 root     root         1024 Jul 17 17:51 RPMS/
drwxr-xr-x   4 root     root         1024 Aug  4 22:31 SOURCES/
drwxr-xr-x   2 root     root         1024 Aug  5 13:12 SPECS/
drwxr-xr-x   2 root     root         1024 Aug  4 22:28 SRPMS/

#
        

Now that we have the directories ready to go, it's time to prepare for the build. For the remainder of this chapter, we'll be building a fictional piece of software known as cdplayer. [38]

Getting the Sources

The first thing we need to do in order to build a package for cdplayer, is to obtain the sources. Being avid cdplayer fans from way back, we know that the latest source can be found at GnomoVision's FTP site, so we go get a copy.

We now have a gzipped tar file of cdplayer version 1.0 on our system. After putting a copy in the SOURCES directory, we're ready to tell RPM what to do with it.

Creating the Spec File

The way we direct RPM in the build process is to create a spec file. As we saw in the previous chapter, the spec file contains eight different sections, most of which are required. Let's go through each section and create cdplayer's spec file as we go.

The Preamble

The preamble contains a wealth of information about the package being built, and the people that built it. Here's cdplayer's preamble:


#
# Example spec file for cdplayer app...
#
Summary: A CD player app that rocks!
Name: cdplayer
Version: 1.0
Release: 1
License: GPL
Group: Applications/Sound
Source: ftp://ftp.gnomovision.com/pub/cdplayer/cdplayer-1.0.tgz
URL: http://www.gnomovision.com/cdplayer/cdplayer.html
Distribution: WSS Linux
Vendor: White Socks Software, Inc.
Packager: Santa Claus <sclaus@northpole.com>

%description
It slices!  It dices!  It's a CD player app that
can't be beat.  By using the resonant frequency
of the CD itself, it is able to simulate 20X
oversampling.  This leads to sound quality that
cannot be equaled with more mundane software...

          

In general, the preamble consists of entries, one per line, that start with a tag followed by a colon, and then some information. For example, the line starting with "Summary:" gives a short description of the packaged software that can be displayed by RPM. The order of the lines is not important, as long as they appear in the preamble.

Let's take a look at each line and see what function it performs:

Name

The name line defines what the package will actually be called. In general, it's a good idea to use the name of the software. The name will also be included in the package label, and the package filename.

Version

The version line should be set to the version of the software being packaged. The version will also be included in the package label, and the package filename.

Release

The release is a number that is used to represent the number of times the software, at the present version, has been packaged. You can think of it as the package's version number. The release is also part of the package label and package filename.

License

The license line is used to hold the packaged software's license information. This makes it easy to determine which packages can be freely redistributed, and which cannot. In our case, cdplayer is made available under the terms of the GNU General Public License, so we've put GPL on the line.

Group

The group line is used to hold a string that defines how the packaged software should be grouped with other packages. The string consists of a series of words separated by slashes. From left to right, the words describe the packaged software more explicitly. We grouped cdplayer under Applications, because it is an application, and then under Sound, since it is an application that is sound-related.

Source

The source line serves two purposes:

  • To document where the packaged software's sources can be found.

  • To give the name of the source file as it exists in the SOURCES subdirectory.

In our example, the cdplayer sources are contained in the file cdplayer-1.0.tgz, which is available from ftp.gnomovision.com, in the directory /pub/cdplayer. RPM actually ignores everything prior to the last filename in the source line, so the first part of the source string could be anything you'd like. Traditionally, the source line usually contains a Uniform Resource Locator, or URL.

URL

The URL line is used to contain a URL, like the source line. How are they different? While the source line is used to provide the source filename to RPM, the URL line points to documentation for the software being packaged.

Distribution

The distribution line contains the name of the product which the packaged software is a part of. In the Linux world, the operating system is often packaged together into a "distribution", hence the name. Since we're using a fictional application in this example, we've filled in the distribution line with the name of a fictional distribution. There's no requirement that the spec file contain a distribution line, so individuals will probably omit this.

Vendor

The vendor line identifies the organization that distributes the software. Maintaining our fictional motif, we've invented fictional company, White Socks Software, to add to our spec file. Individuals will probably omit this as well.

Packager

The packager line is used to identify the organization that actually packaged the software, as opposed to the author of the software. In our example, we've chosen the greatest packager of them all, Santa Claus, to work at White Socks Software. Note that we've included contact information, in the form of an e-mail address.

Description

The description line is a bit different, in that it starts with a percent sign. It is also different because the information can take up more than one line. It is used to provide a more detailed description of the packaged software than the summary line.

A Comment on Comments

At the top of the spec file, there are three lines, each starting with a pound sign. These are comments and can be sprinkled throughout the spec file to make it more easily understood.

The %prep Section

With the preamble, we provided a wealth of information. The majority of this information is meant for human consumption. Only the name, version, release, and source lines have a direct bearing on the package building process. However, in the %prep section, the focus is entirely on directing RPM through the process of preparing the software for building.

It is in the %prep section that the build environment for the software is created, starting with removing the remnants of any previous builds. Following this, the source archive is expanded. Here is what the %prep section looks like in our example spec file:


%prep
rm -rf $RPM_BUILD_DIR/cdplayer-1.0
zcat $RPM_SOURCE_DIR/cdplayer-1.0.tgz | tar -xvf -

          

If the %prep section looks like a script, that's because it is. Any sh constructs can be used here, including expansion of environment variables (Like the $RPM_BUILD_DIR variable defined by RPM), and piping the output of zcat through tar. [39]

In this case, we perform a recursive delete in the build directory to remove any old builds. We then uncompress the gzipped tar file, and extract its contents into the build directory.

Quite often, the sources may require patching in order to build properly. The %prep section is the appropriate place to patch the sources, but in this example, no patching is required. Fear not, however, as we'll explore patching in all its glory in Chapter 20, Real-World Package Building, when we build a more complex package.

Making Life Easier With Macros

While the %prep section as we've described it isn't that difficult to understand, RPM provides macros to make life even easier. In this simple example, there's precious little that can be made easier, but macros will prevent a wealth of headaches when it's time to build more complex packages. The macro we'll introduce here is the %setup macro.

The average gzipped tar file is %setup's stock in trade. Like the hand-crafted %prep section we described above, it cleans up old build trees and then uncompresses and extracts the files from the original source. While %setup has a number of options that we'll cover in later chapters, for now all we need for a %prep section is:


%prep
%setup

            

That is simpler than our %prep section, so let's use the %setup macro instead. The %setup macro has a number of options to handle many different situations. For more information on this and other macros, please see the section called “Macros: Helpful Shorthand for Package Builders”.

In our example here, the %prep section is complete. Next comes the actual build.

The %build Section

Not surprisingly, the part of the spec file that is responsible for performing the build, is the %build section. Like the %prep section, the %build section is an ordinary sh script. Unlike the %prep section, there are no macros. The reason for this is that the process of building software is either going to be very easy, or highly complicated. In either case, macros won't help much. In our example, the build process is simple:


%build
make 

          

Thanks to the make utility, only one command is necessary to build the cdplayer application. In the case of an application with more esoteric build requirements, the %build section could get a bit more interesting.

The %install Section

The %install section is executed as a sh script, just like %prep and %build. If the application is built with make and has an "install" target, the %install section will also be straightforward. The cdplayer application is a good example of this:


%install
make install

          

If the application doesn't have a means of automatically installing itself, it will be necessary to create a script to do so, and place it in the %install section.

The %files Section

The %files section is different from the others, in that it contains a list of the files that are part of the package. Always remember — if it isn't in the file list, it won't be put in the package!


%files
%doc README
/usr/local/bin/cdp
/usr/local/bin/cdplay
/usr/local/man/man1/cdp.1

          

The line starting with %doc is an example of RPM's handling of different file types. As you might guess, %doc stands for documentation. The %doc directive is used to mark files as being documentation. In the example above, the README file will be placed in a package-specific directory, located in /usr/doc, and called cdplayer-1.0-1. It's also possible to mark files as documentation and have them installed in other directories. This is covered in more detail in the section called “The %doc Directive”.

The rest of the files in the example are shown with complete paths. This is necessary as the files will actually be installed in those directories by the application's makefile. Since RPM needs to be able to find the files prior to packaging them, complete paths are required.

How Do You Create the File List?

Since RPM automates so many aspects of software installation, it's easy to fall into the trap of assuming that RPM does everything for you. Not so! One task that is still a manual process is creating the file list. While it may seem at first glance, that it could be automated somehow, it's actually a more difficult problem than it seems.

Since the majority of an application's files are installed by its makefile, RPM has no control over that part of the build process, and therefore, cannot automatically determine which files should be part of the package. Some people have attempted to use a modified version of install that logs the name of every file it installs. But not every makefile uses install, or if it does, uses it sporadically.

Another approach tried was to obtain a list of every file on the build system, immediately before and after a build, and use the differences as the file list. While this approach will certainly find every file that the application installed, it can also pick up extraneous files, such as system logs, files in /tmp, and the like. The only way to begin to make this approach workable would be to do nothing else on the build system, which is highly inconvenient. This approach also precludes building more than one package on the system at any given time.

At present, the best way to create the file list is to read the makefile to see what files it installs, verify this against the files installed on the build system, and create the list.

The Missing Spec File Sections

Since our example spec file is somewhat simplistic, it's missing two sections that might be used in more complex situations. We'll go over each one briefly here. More complete information on these sections will be covered at various points in the book.

The Install/Uninstall Scripts

One missing section to our spec file is the section that would define one or more of four possible scripts. The scripts are executed at various times when a package is installed or erased.

The scripts can be executed:

  • Before a package is installed.

  • After a package is installed.

  • Before a package is erased.

  • After a package is erased.

We'll see how these scripts are used in Chapter 20, Real-World Package Building.

The %clean Section

The other missing section has the rather descriptive title of %clean. This section can be used to clean up any files that are not part of the application's normal build area. For example, if the application creates a directory structure in /tmp as part of its build, it will not be removed. By adding a sh script to the %clean section, such situations can be handled gracefully, right after the binary package is created.

Starting the Build

Now it's time to begin the build. First, we change directory into the directory holding cdplayer's spec file:

# cd /usr/src/redhat/SPECS
#
        

Next, we start the build with an rpmbuild command:

# rpmbuild -ba cdplayer-1.0.spec
        

The a following the -b option directs RPM to perform all phases of the build process. Sometimes it is necessary to stop at various phases during the initial build to resolve problems that crop up while writing the spec file. In these cases, other letters can be used after the -b in order to stop the build at the desired phase. For this example however, we will continue through the entire build process.

In this example, the only other argument to the build command is the name of the package's spec file. This can be wild-carded to build more than one package, but in our example, we'll stick with one.

Let's look at RPM's output during the build:


* Package: cdplayer
+ umask 022
+ echo Executing: %prep
Excuting: %prep
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz
+ tar -xvvf -
drwxrwxr-x root/users        0 Aug  4 22:30 1996 cdplayer-1.0/
-rw-r--r-- root/users    17982 Nov 10 01:10 1995 cdplayer-1.0/COPYING
-rw-r--r-- root/users      627 Nov 10 01:10 1995 cdplayer-1.0/ChangeLog
-rw-r--r-- root/users      482 Nov 10 01:11 1995 cdplayer-1.0/INSTALL
…
-rw-r--r-- root/users     2720 Nov 10 01:10 1995 cdplayer-1.0/struct.h
-rw-r--r-- root/users      730 Nov 10 01:10 1995 cdplayer-1.0/vol.c
-rw-r--r-- root/users     2806 Nov 10 01:10 1995 cdplayer-1.0/volume.c
-rw-r--r-- root/users     1515 Nov 10 01:10 1995 cdplayer-1.0/volume.h
+ [ 0 -ne 0 ]
+ cd cdplayer-1.0
+ cd /usr/src/redhat/BUILD/cdplayer-1.0
+ chown -R root.root .
+ chmod -R a+rX,g-w,o-w .
+ exit 0

        

The output continues, but let's stop here for a moment, and discuss what has happened so far.

At the start of the output, RPM displays the package name (cdplayer), sets the umask, and starts executing the %prep section. Thanks to the %setup macro, RPM then changes directory into the build area, removes any existing old sources, and extracts the sources from the original compressed tar file. Although each file is listed as it is extracted, we've omitted most of the files listed, to save space.

The %setup macro continues by changing directory into cdplayer's top-level source directory and setting the file ownership and permissions properly. As you can see, it does quite a bit of work for you.

Let's take a look at the output from the %build section next:


+ umask 022
+ echo Excuting: %build
Excuting: %build
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make
gcc -Wall -O2  -c -I/usr/include/ncurses  cdp.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  color.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  display.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  misc.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  volume.c 
volume.c: In function `mix_set_volume':
volume.c:67: warning: implicit declaration of function `ioctl'
gcc -Wall -O2  -c -I/usr/include/ncurses  hardware.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  database.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  getline.c 
gcc -o cdp cdp.o color.o display.o misc.o volume.o hardware.o database.o
getline.o  -I/usr/include/ncurses  -L/usr/lib -lncurses
groff -Tascii -man cdp.1 | compress >cdp.1.Z
+ exit 0

        

There are no surprises here. After setting the umask and changing directory into cdplayer's top-level directory, RPM issues the make command we put into the spec file. The rest of the output comes from make as it actually builds the software. Next comes the %install section:


+ umask 022
+ echo Excuting: %install
Excuting: %install
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make install
chmod 755 cdp
chmod 644 cdp.1.Z
cp cdp /usr/local/bin
ln -s /usr/local/bin/cdp /usr/local/bin/cdplay
cp cdp.1 /usr/local/man/man1
+ exit 0

        

Just like the previous sections, RPM again sets the umask and changes directory into the proper directory. It then executes cdplayer's install target, installing the newly built software on the build system. Those of you that carefully studied the spec file might have noticed that the README file is not part of the install section. It's not a problem, as we see here:


+ umask 022
+ echo Excuting: special doc
Excuting: special doc
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ DOCDIR=//usr/doc/cdplayer-1.0-1
+ rm -rf //usr/doc/cdplayer-1.0-1
+ mkdir -p //usr/doc/cdplayer-1.0-1
+ cp -ar README //usr/doc/cdplayer-1.0-1
+ exit 0

        

After the customary umask and cd commands, RPM constructs the path that will be used for cdplayer's documentation directory. It then cleans out any preexisting directory and copies the README file into it. The cdplayer app is now installed on the build system. The only thing left to do is to create the actual package files, and perform some housekeeping. The binary package file is created first:


Binary Packaging: cdplayer-1.0-1
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0
usr/doc/cdplayer-1.0-1
usr/doc/cdplayer-1.0-1/README
usr/local/bin/cdp
usr/local/bin/cdplay
usr/local/man/man1/cdp.1
93 blocks
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm

        

The first line says it all: RPM is creating the binary package for cdplayer version 1.0, release 1. Next, RPM determines what packages are required by cdplayer-1.0-1. Part of this process entails running ldd on each executable program in the package. In this example, the package requires the libraries libc.so.5, and libncurses.so.2.0. Other dependency information can be included in the spec file, but for our example we'll keep it simple.

Following the dependency information, there is a list of every directory and file included in the package. The list displayed is actually the output of cpio, which is the archiving software used by RPM to bundle the package's files. The "93 blocks" is also printed by cpio.

The line "Generating signature: 0" means that RPM has not been directed to add a PGP signature to the package file. During this time, however, RPM still adds two signatures that can be used to verify the size and the MD5 checksum of the package file. Finally, we see confirmation that RPM has created the binary package file.

At this point, the application has been built, and the application's files have been packaged. There is no longer any need for any files created during the build, so they may be removed. In the case of the sources extracted into RPM's build directory, we can see that, at worst, they will be removed the next time the package is built. But what if there were files that we needed to remove? Well, they could be deleted here, in the %clean section:


+ umask 022
+ echo Excuting: %clean
Excuting: %clean
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ exit 0

        

In our example, there are no other files outside of the build directory that are created during cdplayer's build, so we don't need to expend any additional effort to clean things up.

The very last step performed by RPM is to create the source package file:


Source Packaging: cdplayer-1.0-1
cdplayer-1.0.spec
cdplayer-1.0.tgz
80 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

# 
        

This file includes everything needed to recreate a binary package file, as well as a copy of itself. In this example, the only files needed to do that are the original sources and the spec file. In cases where the original sources needed to be modified, the source package includes one or more patch files. As when the binary package was created, we see cpio's output listing each file archived, along with the archive's block size.

Just like a binary package, a source package file can have a PGP signature attached to it. In our case, we see that a PGP signature was not attached. The last message from RPM is to confirm the creation of the source package. Let's take a look at the end products. First, the binary package:

# ls -lF /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm

-rw-r--r--   1 root     root        24698 Aug  6 22:22 RPMS/i386/cdplayer-1.0-1.i386.rpm

#
        

Note that we built cdplayer on an Intel-based system, so RPM placed the binary package files in the i386 subdirectory.

Next, the source package file:

# ls -lF /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

-rw-r--r--   1 root     root        41380 Aug  6 22:22 SRPMS/cdplayer-1.0-1.src.rpm

#
        

Everything went perfectly — we now have binary and source package files ready to use. But sometimes things don't go so well.

When Things Go Wrong

This example is a bit of a fairy tale, in that it went perfectly the first time. In real life, it often takes several tries to get it right.

Problems During the Build

As we alluded to earlier in the chapter, RPM can stop at various points in the build process. This allows package builders to look through the build directory and make sure everything is proceeding properly. If there are problems, stopping during the build process permits them to see exactly what is going wrong, and where. Here is a list of points RPM can be stopped at during the build:

  • After the %prep section.

  • After doing some cursory checks on the %files list.

  • After the %build section.

  • After the %install section.

  • After the binary package has been created.

In addition, there is also a method that permits the package builder to "short circuit" the build process and direct RPM to skip over the initial steps. This is handy when the application is not yet ready for packaging and needs some fine tuning. This way, once the package builds, installs, and operates properly, the required patches to the original sources can be created, and plugged into the package's spec file.

Testing Newly Built Packages

Of course, the fact that an application has been packaged successfully doesn't necessarily mean that it will operate correctly when the package is actually installed. Testing is required. In the case of our example, it's perfect and doesn't need such testing. [40] But here is how testing would proceed:

The first step is to find a test system. If you thought of simply using the build system, bzzzzt, try again! Think about it — in the course of building the package, the build system actually had the application installed on it. That is how RPM gets the files that are to be packaged: by building the software, installing it, and grabbing copies of the installed files, which are found using the %files list.

Some of you dissenters that have read the first half of the book might be thinking, "Why not just install the package on the build system using the --replacefiles option? That way, it'll just blow away the files installed by the build process and replace them with the packaged files." Well, you folks get a bzzzzt, too! Here's why.

Say, for example, that the software you're packaging installs a bunch of files — maybe a hundred. What does this mean? Well for one thing, it means that the package's %files list is going to be quite large. For another thing, the sheer number of files makes it likely that you'll miss one or two. What would happen then?

When RPM builds the software, there's no problem: the software builds, and the application's makefile merrily installs all the files. The next step in RPM's build process is to collect the files by reading the %files list, and to add each file listed to a cpio archive. What happens to the files you've missed? Nothing — they aren't added to the package file, but they are on your build system, installed just where they should be.

Next, when the package is installed using --replacefiles, RPM dutifully installs each of the packaged files, replacing the ones originally installed on the build system. The missed files? They aren't overwritten by RPM since they weren't in the package. But they're still on disk, right where the application expects them to be! If you go to test the application then, it will find every file it needs. But not every file came from the package. Bad news! Using a different system on which the application had never been built is one sure way to test for missing files.

That wraps up our fictional build. Now that we have some experience with RPM's build process, we can take a more in-depth look at RPM's build command.



[38] In reality, this software is a mercilessly hacked version of cdp, which was written by Sariel Har-Peled. The software was hacked to provide a simple example package, and in no way represents the fine work done by Sariel on cdp.

[39] For more information on the environment variables used in the build-time scripts, please refer to the section called “Build-time Scripts”.

[40] Like we said, it's a fairy tale!

Chapter 12. rpmbuild Command Reference

Table 12.1. rpmbuild Command Syntax

rpmbuild -b<stage> options file1.specfileN.spec
<stage> Page
p Execute %prep the section called “ rpmbuild -bp — Execute %prep
c Execute %prep, %build the section called “ rpmbuild -bc — Execute %prep, %build
i Execute %prep, %build, %install, %check the section called “ rpmbuild -bi — Execute %prep, %build, %install, %check
b Execute %prep, %build, %install, %check, package (bin) the section called “ rpmbuild -bb — Execute %prep, %build, %install, %check, package (bin) ”
a Execute %prep, %build, %install, %check, package (bin, src) the section called “ rpmbuild -ba — Execute %prep, %build, %install, %check, package (bin, src) ”
l Check %files list the section called “ rpmbuild -bl — Check %files list ”
Parameters
file1.specfileN.spec One or more .spec files
Build-specific Options Page
--short-circuit Force build to start at particular stage (-bc, -bi only) the section called “ --short-circuit — Force build to start at particular stage ”
--test Create, save build scripts for review the section called “ --test — Create, Save Build Scripts For Review ”
--clean Clean up after build the section called “--clean — Clean up after build”
--sign Add a digital signature to the package the section called “ --sign — Add a Digital Signature to the Package ”
--buildroot <root> Execute %install using <root> as the root the section called “ --buildroot <path> — Execute %install using <path> as the root ”
--buildarch <arch> Perform build for the <arch> architecture the section called “ --buildarch <arch> — Perform Build For the <arch> Architecture ”
--buildos <os> Perform build for the <os> operating system the section called “ --buildos <os> — Perform Build For the <os> Operating System ”
--timecheck <secs> Print a warning if files are over <secs> old the section called “ --timecheck <secs> — Print a warning if files to be packaged are over <secs> old ”
General Options Page
-vv Display debugging information the section called “-vv — Display debugging information”
--quiet Produce as little output as possible the section called “ --quiet — Produce as Little Output as Possible ”
--rcfile <rcfile> Set alternate rpmrc file to <rcfile> the section called “ --rcfile <rcfile> — Set alternate rpmrc file to <rcfile>

rpmbuild — What Does it Do?

When RPM is invoked with the -b option, the process of building a package is started. The rest of the command will determine exactly what is to be built and how far the build should proceed. In this chapter, we'll explore every aspect of rpm -b.

An RPM build command must have two additional pieces of information, over and above "rpmbuild":

  1. The names of one or more spec files representing software to be packaged.

  2. The desired stage at which the build is to stop.

As we discussed in Chapter 10, The Basics of Developing With RPM, the spec file is one of the inputs to RPM's build process. It contains the information necessary for RPM to perform the build and package the software.

There are a number of stages that RPM goes through during a build. By specifying that the build process is to stop at a certain stage, the package builder can monitor the build's progress, make any changes necessary, and restart the build. Let's start by looking at the various stages that can be specified in a build command.

rpmbuild -bp — Execute %prep

The command rpmbuild -bp directs RPM to execute the very first step in the build process. In the spec file, this step is labeled %prep. Every command in the %prep section will be executed when the -bp option is used.

Here's a simple %prep section from the spec file we used in Chapter 11, Building Packages: A Simple Example:


%prep
%setup

          

This %prep section consists of a single %setup macro. When using rpm -bp against this spec file, we can see exactly what %setup does:

# rpmbuild -bp cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz
+ tar -xvvf -
drwxrwxr-x root/users        0 Aug  4 22:30 1996 cdplayer-1.0/
-rw-r--r-- root/users    17982 Nov 10 01:10 1995 cdplayer-1.0/COPYING
-rw-r--r-- root/users      627 Nov 10 01:10 1995 cdplayer-1.0/ChangeLog
…
-rw-r--r-- root/users     2806 Nov 10 01:10 1995 cdplayer-1.0/volume.c
-rw-r--r-- root/users     1515 Nov 10 01:10 1995 cdplayer-1.0/volume.h
+ [ 0 -ne 0 ]
+ cd cdplayer-1.0
+ cd /usr/src/redhat/BUILD/cdplayer-1.0
+ chown -R root.root .
+ chmod -R a+rX,g-w,o-w .
+ exit 0

# 
          

First, RPM confirms that the cdplayer package is the subject of this build. Then it sets the umask and starts executing the %prep section. At this point, the %setup macro is doing its thing. It changes directory into the build area and removes any old copies of cdplayer's build tree.

Next, %setup unzips the sources and uses tar to create the build tree. We've removed the complete listing of files, but be prepared to see lots of output if the software being packaged is large.

Finally, %setup changes directory into cdplayer's build tree and changes ownership and file permissions appropriately. The exit 0 signifies the end of the %prep section, and therefore, the end of the %setup macro. Since we used the -bp option, RPM stopped at this point. Let's see what RPM left in the build area:

# cd /usr/src/redhat/BUILD
# ls -l

total 1
drwxr-xr-x   2 root     root         1024 Aug  4 22:30 cdplayer-1.0

#
          

There's the top-level directory. Changing directory into cdplayer-1.0, we find the sources are ready to be built:

# cd cdplayer-1.0
# ls -lF

total 216
-rw-r--r--   1 root     root        17982 Nov 10  1995 COPYING
-rw-r--r--   1 root     root          627 Nov 10  1995 ChangeLog
…
-rw-r--r--   1 root     root         2806 Nov 10  1995 volume.c
-rw-r--r--   1 root     root         1515 Nov 10  1995 volume.h

# 
          

We can see that %setup's chown and chmod commands did what they were supposed to — the files are owned by root, with permissions set appropriately.

If not stopped by the -bp option, the next step in RPM's build process would be to build the software. RPM can also be stopped at the end of the %build section in the spec file. This is done by using the -bc option:

rpmbuild -bc — Execute %prep, %build

When the -bc option is used during a build, RPM stops once the software has been built. In terms of the spec file, every command in the %build section will be executed. In the following example, we've removed the output from the %prep section to cut down on the redundant output, but keep in mind that it is executed nonetheless:

# rpmbuild -bc cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make
gcc -Wall -O2  -c -I/usr/include/ncurses  cdp.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  color.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  display.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  misc.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  volume.c 
volume.c: In function `mix_set_volume':
volume.c:67: warning: implicit declaration of function `ioctl'
gcc -Wall -O2  -c -I/usr/include/ncurses  hardware.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  database.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  getline.c 
gcc -o cdp cdp.o color.o display.o misc.o volume.o hardware.o database.o
getline.o  -I/usr/include/ncurses  -L/usr/lib -lncurses
groff -Tascii -man cdp.1 | compress >cdp.1.Z
+ exit 0

# 
          

After the command, we see RPM executing the %prep section (which we've removed almost entirely). Next, RPM starts executing the contents of the %build section. In our example spec file, the %build section looks like this:


%build
make 

          

We see that prior to the make command, RPM changes directory into cdplayer's top-level directory. RPM then starts the make, which ends with the groff command. At this point, the execution of the %build section has been completed. Since the -bc option was used, RPM stops at this point.

The next step in the build process would be to install the newly built software. This is done in the %install (and %check) section of the spec file. RPM can be stopped after the install has taken place by using the -bi option:

rpmbuild -bi — Execute %prep, %build, %install, %check

By using the -bi option, RPM is directed to stop once the software is completely built and installed, and the test suite has been run on the build system. Here's what the output of a build using the -bi option looks like:

# rpmbuild -bi cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make install
chmod 755 cdp
chmod 644 cdp.1.Z
cp cdp /usr/local/bin
ln -s /usr/local/bin/cdp /usr/local/bin/cdplay
cp cdp.1 /usr/local/man/man1
+ exit 0
Executing(%check):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make test
All tests run successfully.
+ exit 0
Executing(%doc):
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ DOCDIR=//usr/doc/cdplayer-1.0-1
+ rm -rf //usr/doc/cdplayer-1.0-1
+ mkdir -p //usr/doc/cdplayer-1.0-1
+ cp -ar README //usr/doc/cdplayer-1.0-1
+ exit 0

# 
          

As before, we've excised most of the previously described sections. In this example, the %install section looks like:


%install
make install

          

After the %prep and %build sections, the %install section is executed. Looking at the output, we see that RPM changes directory into cdplayer's top-level directory and issues the make install command, the sole command in the %install section. The output from that point until the first exit 0, is from make install.

The next part of the output is from the %check section, ie. the sole command make test.

The remaining commands are due to the contents of the spec file's %files list. Here's what it looks like:


%files
%doc README
/usr/local/bin/cdp
/usr/local/bin/cdplay
/usr/local/man/man1/cdp.1

          

The line responsible is %doc README. The %doc tag identifies the file as being documentation. RPM handles documentation files by creating a directory in /usr/doc and placing all documentation in it. The exit 0 at the end signifies the end of the %install section. RPM stops due to the -bi option.

The next step at which RPM's build process can be stopped is after the software's binary package file has been created. This is done using the -bb option:

rpmbuild -bb — Execute %prep, %build, %install, %check, package (bin)

# rpmbuild -bb cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
…
+ exit 0
Executing(%check):
…
+ exit 0
Executing(%doc):
…
+ exit 0
Binary Packaging: cdplayer-1.0-1
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0
usr/doc/cdplayer-1.0-1
usr/doc/cdplayer-1.0-1/README
usr/local/bin/cdp
usr/local/bin/cdplay
usr/local/man/man1/cdp.1
93 blocks
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
Executing(%clean):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ exit 0

# 
        

After executing the %prep, %build, %install, and %check sections, and handling any special documentation files, RPM then creates a binary package file. In the sample output, we see that first RPM performs automatic dependency checking. It does this by determining which shared libraries are required by the executable programs contained in the package. Next, RPM actually archives the files to be packaged, optionally signs the package file, and outputs the finished product.

The last part of RPM's output looks suspiciously like a section in the spec file being executed. In our example, there is no %clean section. If there were, however, RPM would have executed any commands in the section. In the absence of a %clean section, RPM simply issues the usual cd commands and exits normally.

rpmbuild -ba — Execute %prep, %build, %install, %check, package (bin, src)

The -ba option directs RPM to perform all the stages in building a package. With this one command, RPM:

  • Unpacks the original sources.

  • Applies patches (if desired).

  • Builds the software.

  • Installs the software.

  • Runs the test suite for the software.

  • Creates the binary package file.

  • Creates the source package file.

That's quite a bit of work for one command! Here it is, in action:

# rpmbuild -ba cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
…
+ exit 0
Executing(%check):
…
+ exit 0
Executing(%doc):
…
+ exit 0
Binary Packaging: cdplayer-1.0-1
…
Executing(%clean):
…
+ exit 0
Source Packaging: cdplayer-1.0-1
cdplayer-1.0.spec
cdplayer-1.0.tgz
80 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

# 
          

As in previous examples, RPM executes the %prep, %build, %install, and %check sections, handles any special documentation files, creates a binary package file, and cleans up after itself.

The final step in the build process is to create a source package file. As the output shows, it consists of the spec file and the original sources. A source package may optionally include one or more patch files, although in our example, cdplayer requires none.

At the end of a build using the -ba option, the software has been successfully built and packaged in both binary and source form. But there are a few more build-time options that we can use. One of them is the -bl option:

rpmbuild -bl — Check %files list

There's one last letter that may be specified with rpm -b, but unlike the others, which indicate the stage at which the build process is to stop, this option performs a variety of checks on the %files list in the named spec file. When l is added to rpmbuild, the following checks are performed:

  • Expands the spec file's %files list and checks that each file listed actually exists.

  • Determines what shared libraries the software requires by examining every executable file listed.

  • Determines what shared libraries are provided by the package.

Why is it necessary to do all this checking? When would it be useful? Keep in mind that the %files list must be generated manually. By using the -bl option, the following steps are all that's necessary to create a %files list:

  • Writing the %files list.

  • Using the -bl option to check the %files list.

  • Making any necessary changes to the %files list.

It may take more than one iteration through these steps, but eventually the list check will pass. Using the -bl option to check the %files list is certainly better than starting a two-hour package build, only to find out at the very end that the list contains a misspelled filename.

Here's an example of the -bl option in action:

# rpmbuild -bl cdplayer-1.0.spec

* Package: cdplayer
File List Check: cdplayer-1.0-1
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0

#
          

It's hard to see exactly what RPM is doing from the output, but if we add -vv, we can get a bit more information:

# rpmbuild -bl -vv cdplayer-1.0.spec

D: Switched to BASE package
D: Source(0) = sunsite.unc.edu:/pub/Linux/apps/sound/cds/cdplayer-1.0.tgz
D: Switching to part: 12
D: fileFile = 
D: Switched to package: (null)
D: Switching to part: 2
D: fileFile = 
D: Switching to part: 3
D: fileFile = 
D: Switching to part: 4
D: fileFile = 
D: Switching to part: 10
D: fileFile = 
D: Switched to package: (null)
* Package: cdplayer
File List Check: cdplayer-1.0-1
D: ADDING: /usr/doc/cdplayer-1.0-1
D: ADDING: /usr/doc/cdplayer-1.0-1/README
D: ADDING: /usr/local/bin/cdp
D: ADDING: /usr/local/bin/cdplay
D: ADDING: /usr/local/man/man1/cdp.1
D: md5(/usr/doc/cdplayer-1.0-1/README) = 2c149b2fb1a4d65418131a19b242601c
D: md5(/usr/local/bin/cdp) = 0f2a7a2f81812c75fd01c52f456798d6
D: md5(/usr/local/bin/cdplay) = d41d8cd98f00b204e9800998ecf8427e
D: md5(/usr/local/man/man1/cdp.1) = b32cc867ae50e2bdfa4d6780b084adfa
Finding dependencies...
D: Adding require: libncurses.so.2.0
D: Adding require: libc.so.5
Requires (2): libc.so.5 libncurses.so.2.0

# 
          

Looking at this more verbose output, it's easy to see there's a great deal going on. Some of it is not directly pertinent to checking the %files list, however. For example, the output extending from the first line, to the line reading * Package: cdplayer, reflects processing that takes place during actual package building, and can be ignored.

Following that section is the actual %files list check. In this section, every file named in the %files list is checked to make sure it exists. The phrase, ADDING:, again reflects RPM's package building roots. When using the -bl option, however, RPM is simply making sure the files exist on the build system. If the --timecheck option (described a bit later, on the section called “ --timecheck <secs> — Print a warning if files to be packaged are over <secs> old ”) is present, the checks required by that option are performed here, as well.

After the list check, the MD5 checksums of each file are calculated and displayed. While this information is vital during actual package building, it is not used when using the -bl option.

Finally, RPM determines which shared libraries the listed files require. In this case, there are only two — libc.so.5, and libncurses.so.2.0. While not strictly a part of the list-checking process, displaying shared library dependencies can be quite helpful at this point. It can point out possible problems, such as assuming that the target systems have a certain library installed when, in fact, they do not.

So far, we've only seen what happens when the %files list is correct. Let's see what happens where the list has problems. In this example, we've added a bogus file to the package's %files list:

# rpmbuild -bl cdplayer-1.0.spec

* Package: cdplayer
File List Check: cdplayer-1.0-1
File not found: /usr/local/bin/bogus
Build failed.

#
          

Reflecting more of its package building roots, rpm -bl says that the "build failed". But the bottom line is that there is no such file as /usr/bin/bogus. In this example we made the name obviously wrong, but in a more real-world setting, the name will more likely be a misspelling in the %files list. OK, let's correct the %files list and try again:

# rpmbuild -bl cdplayer-1.0.spec

* Package: cdplayer
File List Check: cdplayer-1.0-1
File not found: /usr/local/bin/cdplay
Build failed.

#
          

Another error! In this case the file is spelled correctly, but it is not on the build system, even though it should be. Perhaps it was deleted accidentally. In any case, let's rebuild the software and try again:

# rpmbuild -bi cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
…
ln -s /usr/local/bin/cdp /usr/local/bin/cdplay
…
+ exit 0
Executing(%check):
…
+ exit 0
Executing(%doc):
…
+ exit 0

# 
# rpmbuild -bl cdplayer-1.0.spec

* Package: cdplayer
File List Check: cdplayer-1.0-1
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0

#
          

Done! The moral to this story is that using rpm -bl and fixing the error it flagged doesn't necessarily mean your %files list is ready for prime-time: Always run it again to make sure!

--short-circuit — Force build to start at particular stage

Although it sounds dangerous, the --short-circuit option can be your friend. This option is used during the initial development of a package. Earlier in the chapter, we explored stopping RPM's build process at different stages. Using --short-circuit, we can start the build process at different stages.

One time that --short-circuit comes in handy is when you're trying to get software to build properly. Just think what it would be like — you're hacking away at the sources, trying a build, getting an error, and hacking some more to fix that error. Without --short-circuit, you'd have to:

  1. Make your change to the sources.

  2. Use tar to create a new source archive.

  3. Start a build with something like rpmbuild -bc.

  4. See another bug.

  5. Go back to step 1.

Pretty cumbersome! Since RPM's build process is designed to start with the sources in their original tar file, unless your modifications end up in that tar file, they won't be used in the next build. [41]

But there's another way. Just follow these steps:

  1. Place the original source tar file in RPM's SOURCES directory.

  2. Create a partial spec file in RPM's SPECS directory (Be sure to include a valid Source line).

  3. Issue an rpmbuild -bp to properly create the build environment.

Now use --short-circuit to attempt a compile. Here's an example:

# rpmbuild -bc --short-circuit cdplayer-1.0.spec

* Package: cdplayer
Executing(%build):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make
gcc -Wall -O2  -c -I/usr/include/ncurses  cdp.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  color.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  display.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  misc.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  volume.c 
volume.c: In function `mix_set_volume':
volume.c:67: warning: implicit declaration of function `ioctl'
gcc -Wall -O2  -c -I/usr/include/ncurses  hardware.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  database.c 
gcc -Wall -O2  -c -I/usr/include/ncurses  getline.c 
gcc -o cdp cdp.o color.o display.o misc.o volume.o
     hardware.o database.o getline.o  -I/usr/include/ncurses
     -L/usr/lib -lncurses
groff -Tascii -man cdp.1 | compress >cdp.1.Z
+ exit 0

# 
          

Normally, the -bc option instructs RPM to stop the build after the %build section of the spec file has been executed. By adding --short-circuit, however, RPM starts the build by executing the %build section and stops when everything in %build has been executed.

There is only one other build stage that can be --short-circuit'ed, and that is the install stage. The reason for this restriction is to make it difficult to bypass RPM's use of pristine sources. If it were possible to --short-circuit to -bb or -ba, a package builder might take the "easy" way out and simply hack at the build tree until the software built successfully, then package the hacked sources. So, RPM will only --short-circuit to -bc or -bi. Nothing else will do.

What exactly does an rpmbuild -bi --short-circuit do, anyway? Like an rpmbuild -bc --short-circuit, it starts executing at the named stage, which in this case is %install. Note that the build environment must be ready to perform an install before attempting to --short-circuit to the %install stage. If the software installs via make install, make will automatically compile the software anyway.

And what happens if the build environment isn't ready and a --short-circuit is attempted? Let's see:

# rpmbuild -bi --short-circuit cdplayer-1.0.spec

* Package: cdplayer
Executing(%install):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
/var/tmp/rpmbu01157aaa: cdplayer-1.0: No such file or directory
Bad exit status

# 
          

RPM blindly started executing the %install stage, but came to an abrupt halt when it attempted to change directory into cdplayer-1.0, which didn't exist. After giving a descriptive error message, RPM exited with a failure status. Except for some minor differences, rpmbuild -bc would have failed in the same way.

--buildarch <arch> — Perform Build For the <arch> Architecture

The --buildarch option is used to override RPM's architecture detection logic. The option is followed by the desired architecture name. Here's an example:

# rpmbuild -ba --buildarch i486 cdplayer-1.0.spec

* Package: cdplayer
…
Binary Packaging: cdplayer-1.0-1
…
Wrote: /usr/src/redhat/RPMS/i486/cdplayer-1.0-1.i486.rpm
…
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

#
          

We've removed most of RPM's output from this example, but the main thing we can see from this example is that the package was built for the i486 architecture, due to the inclusion of the --buildarch option on the command line. We can also see that RPM wrote the binary package in the architecture-specific directory, /usr/src/redhat/RPMS/i486. Using RPM's --queryformat option confirms the package's architecture:

# rpmquery -qp --queryformat '%{arch}\n' /usr/src/redhat/RPMS/i486/cdplayer-1.0-1.i486.rpm

i486

#
          

For more information on build packages for multiple architectures, please see Chapter 19, Building Packages for Multiple Architectures and Operating Systems .

--buildos <os> — Perform Build For the <os> Operating System

The --buildos option is used to override RPM's operating system detection logic. The option is followed by the desired operating system name. Here's an example:

# rpmbuild -ba --buildos osf1 cdplayer-1.0.spec

…
Binary Packaging: cdplayer-1.0-1
…
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
Source Packaging: cdplayer-1.0-1
…
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

#
          

There's nothing in the build output that explicitly states the build operating system as been set to osf1. Let's see if --queryformat will tell us:

# rpmquery -qp --queryformat '%{os}\n' /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm

osf1

#
          

The package was indeed built for the specified operating system. For more information on building packages for multiple operating systems, please see Chapter 19, Building Packages for Multiple Architectures and Operating Systems .

--sign — Add a Digital Signature to the Package

The --sign option directs RPM to add a digital signature to the package being built. Currently, this is done using PGP. Here's an example of --sign in action:

# rpmbuild -ba --sign cdplayer-1.0.spec

Enter pass phrase: passphrase (not echoed)
Pass phrase is good.
* Package: cdplayer
…
Binary Packaging: cdplayer-1.0-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
…
Source Packaging: cdplayer-1.0-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

#
          

The most obvious effect of adding the --sign option to a build command is that RPM then asks for your private key's passphrase. After entering the passphrase (which isn't echoed), the build proceeds as usual. The only other difference between this and a non-signed build is that the Generating signature: lines have a non-zero value.

Let's check the source and binary packages we've just created and see if they are, in fact, signed:

# rpmsign --checksig /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

/usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm: size pgp md5 OK

# rpmsign --checksig /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm

/usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm: size pgp md5 OK

#
          

The fact that there is a pgp in --checksig's output indicates that the packages have been signed.

For more information on signing packages, please see Chapter 17, Adding PGP Signatures to a Package. Also, Appendix G, An Introduction to PGP contains information on obtaining and installing PGP.

--test — Create, Save Build Scripts For Review

There are times when it might be necessary to get a more in-depth view of a particular build. By using the --test option, it's easy. When --test is added to a build command, the scripts RPM would normally use to actually perform the build, are created and saved for you to review. Let's see how it works:

# rpmbuild -ba --test cdplayer-1.0.spec

* Package: cdplayer

#
          

Unlike a normal build, there's not much output. But the --test option has caused a set of scripts to be written and saved for you. The question is: Where are they?

If you are using a customized rpmrc file, the scripts will be written to the directory specified by the rpmrc entry tmppath. If you haven't changed this setting, RPM, by default, writes the scripts in /var/tmp. Here they are:

# ls -l /var/tmp

total 4
-rw-rw-r--   1 root     root          670 Sep 17 20:35 rpmbu00236aaa
-rw-rw-r--   1 root     root          449 Sep 17 20:35 rpmbu00236baa
-rw-rw-r--   1 root     root          482 Sep 17 20:35 rpmbu00236caa
-rw-rw-r--   1 root     root          552 Sep 17 20:35 rpmbu00236daa

# 
          

Each file contains a script that performs a given part of the build. Here's the first file:


#!/bin/sh -e
# Script generated by rpm

RPM_SOURCE_DIR="/usr/src/redhat/SOURCES"
RPM_BUILD_DIR="/usr/src/redhat/BUILD"
RPM_DOC_DIR="/usr/doc"
RPM_OPT_FLAGS="-O2 -m486 -fno-strength-reduce"
RPM_ARCH="i386"
RPM_OS="Linux"
RPM_ROOT_DIR="/tmp/cdplayer"
RPM_BUILD_ROOT="/tmp/cdplayer"
RPM_PACKAGE_NAME="cdplayer"
RPM_PACKAGE_VERSION="1.0"
RPM_PACKAGE_RELEASE="1"
set -x

umask 022

echo Executing(%prep)
cd /usr/src/redhat/BUILD

cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

          

As we can see, this script contains the %prep section from the spec file. The script starts off by defining a number of environment variables and then leads into the %prep section. In the spec file used in this build, the %prep section consists of a single %setup macro. In this file, we can see exactly how RPM expands that macro. The remaining files follow the same basic layout — a section defining environment variables, followed by the commands to be executed.

Note that the --test option will only create script files for each build stage, as specified in the command line. For example, if the above command was changed to:

# rpmbuild -bp --test cdplayer-1.0.spec
#
          

only one script file, containing the %prep commands, would be written. In any case, no matter what RPM build command is used, the --test option can let you see exactly what is going to happen during a build.

--clean — Clean up after build

The --clean option can be used to ensure that the package's build directory tree is removed at the end of a build. Although it can be used with any build stage, it doesn't always make much sense to do so:

# rpmbuild -bp --clean cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(--clean):
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ exit 0

# 
          

In this example, we see a typical %prep section being executed. The line "Executing(--clean):" indicates the start of the --clean's activity. After changing directory into the build directory, RPM then issues a recursive delete on the package's top-level directory.

As we noted above, this particular example doesn't make much sense. We're only executing the %prep section, which creates the package's build tree, and using --clean, which removes it! Using --clean with the -bc option isn't very productive either, as the newly built software remains in the build tree. Once again, there would be no remnants left after --clean has done its thing.

Normally, the --clean option is used once the software builds and can be packaged successfully. It is particularly useful when more than one package is to be built, since --clean ensures that the filesystem holding the build area will not fill up with build trees from each package.

Note also that the --clean option only removes the files that reside in the software's build tree. If there are any files that the build creates outside of this hierarchy, it will be necessary to write a script for the spec file's %clean section.

--buildroot <path> — Execute %install using <path> as the root

The --buildroot option can make two difficult situations much easier:

  • Performing a build without impacting the build system.

  • Allowing non-root users to build packages.

Let's study the first situation in a bit more detail. Say, for example, that sendmail is to be packaged. In the course of creating a sendmail package, the software must be installed. This would mean that critical sendmail files, such as sendmail.cf and aliases, would be overwritten. Mail handling on the build system would almost certainly be disrupted.

In the second case, it's certainly possible to set permissions such that non-root users can install software, but highly unlikely that any system administrator worth their salt would do so. What can be done to make these situations more tenable?

The --buildroot option is used to instruct RPM to use a directory other than / as a "build root". This phrase is a bit misleading, in that the build root is not the root directory under which the software is built. Rather, it is the root directory for the install phase of the build. When a build root is not specified, the software being packaged is installed relative to the build system's root directory "/".

However, it's not enough to just specify a build root on the command line. The spec file for the package must be set up to support a build root. If you don't make the necessary changes, this is what you'll see:

# rpmbuild -ba --buildroot /tmp/foo cdplayer-1.0.spec

Package can not do build prefixes
Build failed.

#
          

Chapter 16, Making a Package That Can Build Anywhere has complete instructions on the modifications necessary to configure a package to use an alternate build root, as well as methods to permit users to build packages without root access. Assuming that the necessary modifications have been made, here is what the build would look like:

# rpmbuild -ba --buildroot /tmp/foonly cdplayer-1.0.spec

* Package: cdplayer
Executing(%prep):
+ cd /usr/src/redhat/BUILD
…
+ exit 0
Executing(%build):
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
…
+ exit 0
Executing(%install):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ make ROOT=/tmp/foonly install
install -m 755 -o 0 -g 0 -d /tmp/foonly/usr/local/bin/
install -m 755 -o 0 -g 0 cdp /tmp/foonly/usr/local/bin/cdp
rm -f /tmp/foonly/usr/local/bin/cdplay
ln -s /tmp/foonly/usr/local/bin/cdp /tmp/foonly/usr/local/bin/cdplay
install -m 755 -o 0 -g 0 -d /tmp/foonly/usr/local/man/man1/
install -m 755 -o 0 -g 0 cdp.1 /tmp/foonly/usr/local/man/man1/cdp.1
+ exit 0
Executing(%check):
+ umask 022
…
+ exit 0
Executing(%doc):
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ DOCDIR=/tmp/foonly//usr/doc/cdplayer-1.0-1
+ rm -rf /tmp/foonly//usr/doc/cdplayer-1.0-1
+ mkdir -p /tmp/foonly//usr/doc/cdplayer-1.0-1
+ cp -ar README /tmp/foonly//usr/doc/cdplayer-1.0-1
+ exit 0
Binary Packaging: cdplayer-1.0-1
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0
usr/doc/cdplayer-1.0-1
usr/doc/cdplayer-1.0-1/README
usr/local/bin/cdp
usr/local/bin/cdplay
usr/local/man/man1/cdp.1
93 blocks
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
Executing(%clean):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ exit 0
Source Packaging: cdplayer-1.0-1
cdplayer-1.0.spec
cdplayer-1.0.tgz
82 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm

# 
          

As the somewhat edited output shows, the %prep, %build, and %install sections are executed in RPM's normal build directory. However, the --buildroot option comes into play when the make install is done. As we can see, the ROOT variable is set to /tmp/foonly, which was the value following --buildroot on the command line. From that point on, we can see that make substituted the new build root value during the install phase.

The build root is also used when documentation files are installed. The documentation directory cdplayer-1.0-1 is created in /tmp/foonly/usr/doc, and the README file is placed in it.

The only remaining difference that results from using --buildroot, is that the files to be included in the binary package are not located relative to the build system's root directory. Instead they are located relative to the build root /tmp/foonly. The resulting binary and source package files are functionally equivalent to packages built without the use of --buildroot.

Using --buildroot Can Bite You!

Although the --buildroot option can solve some problems, using a build root can actually be dangerous. How? Consider the following situation:

  • A spec file is configured to have a build root of /tmp/blather, for instance.

  • In the %prep section [42] , there is an rm -rf $RPM_BUILD_ROOT command to clean out any old installed software.

  • You decide to build the software so that it installs relative to your system's root directory, so you enter the following command: "rpmbuild -ba --buildroot / foo.spec".

The end result? Since specifying "/" as the build root sets $RPM_BUILD_ROOT to "/", that innocuous little rm -rf $RPM_BUILD_ROOT turns into rm -rf /! A recursive delete, starting at your system's root directory, might not be a total disaster if you catch it quickly, but in either case, you'll be testing your ability to restore from backup… Er, you do have backups, don't you?

The moral of this story is to be very careful when using --buildroot. A good rule of thumb is to always specify a unique build root. For example, instead of specifying /tmp as a build root (and possibly losing your system's directory for holding temporary files), use the path /tmp/mypackage, where the directory mypackage is used only by the package you're building.

--timecheck <secs> — Print a warning if files to be packaged are over <secs> old

While it's possible to detect many errors in the %files list using rpmbuild -bl, there is another type of problem that can't be detected. Consider the following scenario:

  • A package you're building creates the file /usr/bin/foo.

  • Because of a problem with the package's makefile, foo is never copied into /usr/bin.

  • An older, incompatible version of foo, created several months ago, already exists in /usr/bin.

  • RPM creates the binary package file.

Is the incompatible /usr/bin/foo included in the package? You bet it is! If only there was some way for RPM to catch this type of problem…

Well, there is! By adding --timecheck, followed by a number, RPM will check each file being packaged, to see if the file is more than the specified number of seconds old. If it is, a warning message is displayed. The --timecheck option works with either the -ba or -bl options. Here's an example using -bl:

# rpmbuild -bl --timecheck 3600 cdplayer-1.0.spec

* Package: cdplayer
File List Check: cdplayer-1.0-1
warning: TIMECHECK failure: /usr/doc/cdplayer-1.0-1/README
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0

# 
          

In this example, the file /usr/doc/cdplayer-1.0-1/README is more than 3,600 seconds, or one hour, old. If we take a look at the file, we find that it is: [43]

# ls -al /usr/doc/cdplayer-1.0-1/README

-rw-r--r--   1 root     root         1085 Nov 10  1995 README

#
          

In this particular case, the warning from --timecheck is no cause for alarm. Since the README file was simply copied from the original source, which was created November 10th, 1995, its date is unchanged. If the file had been an executable or a library that was supposedly built recently, --timecheck's warning should be taken more seriously.

If you'd like to set a default time check value of one hour, you can include the following line in your rpmrc file:


timecheck: 3600

          

This value can still be overridden by a value on the command line, if desired. For more information on the use of rpmrc files, see Appendix B, The rpmrc File.

-vv — Display debugging information

Unlike most other RPM commands, there is no -v option for rpmbuild. That's because the command's default is to be verbose. However, even more information can be obtained by adding -vv. Here's an example:

# rpmbuild -bp -vv cdplayer-1.0.spec

D: Switched to BASE package
D: Source(0) = sunsite.unc.edu:/pub/Linux/apps/sound/cds/cdplayer-1.0.tgz
D: Switching to part: 12
D: fileFile = 
D: Switched to package: (null)
D: Switching to part: 2
D: fileFile = 
D: Switching to part: 3
D: fileFile = 
D: Switching to part: 4
D: fileFile = 
D: Switching to part: 10
D: fileFile = 
D: Switched to package: (null)
* Package: cdplayer
D: RUNNING: %prep
Executing(%prep):
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz
+ tar -xvvf -
drwxrwxr-x root/users        0 Aug  4 22:30 1996 cdplayer-1.0/
-rw-r--r-- root/users    17982 Nov 10 01:10 1995 cdplayer-1.0/COPYING
…
-rw-r--r-- root/users     1515 Nov 10 01:10 1995 cdplayer-1.0/volume.h
+ [ 0 -ne 0 ]
+ cd cdplayer-1.0
+ cd /usr/src/redhat/BUILD/cdplayer-1.0
+ chown -R root.root .
+ chmod -R a+rX,g-w,o-w .
+ exit 0

# 
          

Most of the output generated by the -vv option is preceded by a D:. In this example, the additional output represents RPM's internal processing during the start of the build process. Using the -vv option with other build commands will produce different output.

--quiet — Produce as Little Output as Possible

As we mentioned above, the build command is normally verbose. The --quiet option can be used to cut down on the command's output:

# rpmbuild -ba --quiet cdplayer-1.0.spec

* Package: cdplayer
volume.c: In function `mix_set_volume':
volume.c:67: warning: implicit declaration of function `ioctl'
90 blocks
82 blocks

#
          

This is the entire output from a package build of cdplayer. Note that warning messages (actually, anything sent to stdout) are still printed.

--rcfile <rcfile> — Set alternate rpmrc file to <rcfile>

The --rcfile option is used to specify a file containing default settings for RPM. Normally, this option is not needed. By default, RPM uses /etc/rpmrc and a file named .rpmrc located in your login directory.

This option would be used if there was a need to switch between several sets of RPM defaults. Software developers and package builders will normally be the only people using the --rcfile option. For more information on rpmrc files, see Appendix B, The rpmrc File.

Other Build-related Commands

There are two other commands that also perform build-related functions. However, they do not use the rpmbuild command syntax that we've been studying so far. Instead of specifying the name of the spec file, as with rpmbuild, it's necessary to specify the name of the source package file.

Why the difference in syntax? The reason has to do with the differing functions of these commands. Unlike rpmbuild, where the name of the game is to get software packaged into binary and source package files, these commands use an already-existing source package file as input. Let's take a look at them:

rpmbuild --recompile — What Does it Do?

The --recompile option directs RPM to perform the following steps:

  • Install the specified source package file.

  • Unpack the original sources.

  • Build the software.

  • Install the software.

  • Run the tests.

While you might think this sounds a great deal like an install of the source package file, followed by an rpmbuild -bi, this is not entirely the case. Using --recompile, the only file required is the source package file. After the software is built and installed, the only thing left, other than the newly installed software, is the original source package file.

The --recompile option is normally used when a previously installed package needs to be recompiled. --recompile comes in handy when software needs to be compiled against a new version of the kernel.

Here's what RPM displays during a --recompile:

# rpmbuild --recompile cdplayer-1.0-1.src.rpm

Installing cdplayer-1.0-1.src.rpm
* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
…
+ exit 0
Executing(%check):
…
+ exit 0
Executing(%doc):
…
+ exit 0

# 
          

The very first line shows RPM installing the source package. After that are ordinary executions of the %prep, %build, and %install sections of the spec file.

Since rpm -i or rpm -U are not being used to install the software, the RPM database is not updated during a --recompile. This means that doing a --recompile on an already-installed package may result in problems down the road, when RPM is used to upgrade or verify the package.

rpmbuild --rebuild — What Does it Do?

Package builders, particularly those that create packages for multiple architectures, often need to build their packages starting from the original sources. The --rebuild option does this, starting from a source package file. Here is the list of steps it performs:

  • Install the specified source package file.

  • Unpack the original sources.

  • Build the software.

  • Install the software.

  • Run the tests.

  • Create a binary package file.

  • Remove the software's build directory tree and run the %clean script.

Unlike the --recompile option, --rebuild cleans up after itself. The other difference between the two commands is the fact that --rebuild also creates a binary package file. The only remnants of a --rebuild are the original source package, the newly installed software, and a new binary package file.

Package builders find this command especially handy, as it allows them to create new binary packages using one command, with no additional cleanups required. There are several times when --rebuild is normally used:

  • When the build environment (eg. compilers, libraries, etc.) has changed.

  • When binary packages for a different architecture are to be built.

Here's an example of the --rebuild option in action:

# rpmbuild --rebuild cdplayer-1.0-1.src.rpm

Installing cdplayer-1.0-1.src.rpm
* Package: cdplayer
Executing(%prep):
…
+ exit 0
Executing(%build):
…
+ exit 0
Executing(%install):
…
+ exit 0
Executing(%check):
…
+ exit 0
Executing(%doc):
…
+ exit 0
Binary Packaging: cdplayer-1.0-1
…
Executing(%clean):
…
+ exit 0
Executing(--clean):
…
+ exit 0

#
          

The very first line shows RPM installing the source package. The lines after that are ordinary executions of the %prep, %build, %install and %check (if any) sections of the spec file. Next, a binary package file is created. Finally, the spec file's %clean section (if one exists) is executed. The cleanup of the software's build directory takes place, just as if the --clean option had been specified.

That completes our overview of the commands used to build packages with RPM. In the next chapter, we'll look at the various macros that are available and how they can make life easier for the package builder.



[41] As we mentioned in Chapter 10, The Basics of Developing With RPM, if the original sources need to be modified, the modifications should be kept as a separate set of patches. However, during development, it makes more sense to not generate patches every time a change to the original source is made.

[42] Or the %clean section, it doesn't matter — the end result is the same.

[43] It should be noted that the package was built substantially later than November of 1995!

Chapter 13. Inside the Spec File

In this chapter, we're going to cover the spec file in detail. There are a number of different types of entries that comprise a spec file, and every one will be documented here. The different types of entries are:

  • Comments — Human-readable notes ignored by RPM.

  • Tags — Define data.

  • Scripts — Contain commands to be executed at specific times.

  • Macros — A method of executing multiple commands easily.

  • The %files list — A list of files to be included in the package.

  • Directives — Used in the %files list to direct RPM to handle certain files in a specific way.

  • Conditionals — Permit operating system- or architecture-specific preprocessing of the spec file.

Let's start by looking at comments.

Comments: Notes Ignored by RPM

Comments are a way to make RPM ignore a line in the spec file. The contents of a comment line are entirely up to the person writing the spec file.

To create a comment, enter an octothorp (#) at the start of the line. Any text following the comment character will be ignored by RPM. Here's an example comment:


# This is the spec file for playmidi 2.3...

        

Comments can be placed in any section of the spec file. Note that macros are expanded everywhere, so with multiline macros which would only have the first line commented also escape the percent (%) character:


# %%configure

        

Tags: Data Definitions

Looking at a spec file, the first thing you'll see are a number of lines, all following the same basic format:


<something>:<something-else>

        

The <something> is known as a "tag", because it is used by RPM to name or tag some data. The tag is separated from its associated data by a colon. The data is represented by the <something-else> above. Tags are grouped together at the top of the spec file, in a section known as the preamble. Here's an example of a tag and its data:


Vendor: White Socks Software, Inc.

        

In this example, the tag is "Vendor". Tags are not case-sensitive — they may be all uppercase, all lowercase, or anything in-between. The Vendor tag is used to define the name of the organization producing the package. The data in this example is "White Socks Software, Inc.". Therefore, RPM will store White Socks Software, Inc. as the vendor of the package.

Note, also, that spacing between the tag, the colon, and the data is unimportant. Given this, and the case-insensitivity of the tag, each of the following lines are equivalent to the one above:


VeNdOr : White Socks Software, Inc.
vendor:White Socks Software, Inc.
VENDOR    :    White Socks Software, Inc.

        

The bottom line is that you can make tag lines as neat or as ugly as you like — RPM won't mind either way. Note, however, the tag's data may need to be formatted in a particular fashion. If there are any such restrictions, we'll mention them. Below, we've grouped tags of similar functions together for easier reference, starting with the tags that are used to create the package name.

Package Naming Tags

The following tags are used by RPM to produce the package's final name. Since the name is always in the format:


<name>-<version>-<release>

          

it's only natural that the three tags are known as name, version, and release.

The name Tag

The name tag is used to define the name of the software being packaged. In most (if not all) cases, the name used for a package should be identical in spelling and case to the software being packaged. The name cannot contain any whitespace: If it does, RPM will only use the first part of the name (up to the first space). Therefore, if the name of the software being packaged is cdplayer, the name tag should be something like:


Name: cdplayer

            

The version Tag

The version tag defines the version of the software being packaged. The version specified should be as close as possible to the format of the original software's version. In most cases, there should be no problem specifying the version just as the software's original developer did. However, there is a restriction. There can be no dashes in the version. If you forget, RPM will remind you:

# rpmbuild -ba cdplayer-1.0.spec

* Package: cdplayer
Illegal '-' char in version: 1.0-a

#
            

Spaces in the version will also cause problems, in that anything after the first space will be ignored by RPM. Bottom line: Stick with alphanumeric characters and periods, and you'll never have to worry about it. Here's a sample version tag:


Version: 1.2

            

The release Tag

The release tag can be thought of as the package's version. The release is traditionally an integer — for example, when a specific piece of software at a particular version is first packaged, the release should be "1". If it is necessary to repackage that software at the same version, the release should be incremented. When a new version of the software becomes available, the release should drop back to "1" when it is first packaged.

Note that we used the word "traditionally", above. The only hard and fast restriction to the release format is that there can be no dashes in it. Be aware that if you buck tradition, your users may not understand what your release means.

It is up to the package builder to determine which build represents a new release and to update the release manually. Here is what a typical release tag might look like:


Release: 5

            

Descriptive Tags

These tags provide information primarily for people who want to know a bit more about the package, and who produced it. They are part of the package file, and most of them can be seen by issuing an rpm -qi command.

The %description Tag

The %description tag is used to provide an in-depth description of the packaged software. The description should be several sentences describing, to an uninformed user, what the software does.

The %description tag is a bit different than the other tags in the preamble. For one, it starts with a percent sign. The other difference is that the data specified by the %description tag can span more than one line. In addition, a primitive formatting capability exists. If a line starts with a space, that line will be displayed verbatim by RPM. Lines that do not start with a space are assumed to be part of a paragraph and will be formatted by RPM. It's even possible to mix and match formatted and unformatted lines. Here are some examples:


%description
It slices!  It dices!  It's a CD player app that can't be beat.  By using
the resonant frequency of the CD itself, it is able to simulate 20X
oversampling.  This leads to sound quality that cannot be equaled with
more mundane software...

            

The example above contains no explicit formatting. RPM will format the text as a single paragraph, breaking lines as needed.


%description
 It slices!
 It dices!
 It's a CD player app that can't be beat.
By using the resonant frequency of the CD itself, it is able to simulate
20X oversampling.  This leads to sound quality that cannot be equaled with
more mundane software...

            

In this example, the first three lines will be displayed by RPM, verbatim. The remainder of the text will be formatted by RPM. The text will be formatted as one paragraph.


%description
 It slices!
 It dices!
 It's a CD player app that can't be beat.

By using the resonant frequency of the CD itself, it is able to simulate
20X oversampling.  This leads to sound quality that cannot be equaled with
more mundane software...

            

Above, we have a similar situation to the previous example, in that part of the text is formatted and part is not. However, the blank line separates the text into two paragraphs.

The summary Tag

The summary tag is used to define a one-line description of the packaged software. Unlike %description, summary is restricted to one line. RPM uses it when a succinct description of the package is needed. Here is an example of a summary line:


Summary: A CD player app that rocks!

            

The license Tag

The license tag is used to define the license terms applicable to the software being packaged. This tag is also known as the copyright tag. In many cases, this might be nothing more than "GPL", for software distributed under the terms of the GNU General Public License, or something similar. For example:


License: GPL

            

The distribution Tag

The distribution tag is used to define a group of packages, of which this package is a part. Since Red Hat is in the business of producing a group of packages known as a Linux distribution, the name stuck. For example, if a suite of applications known as "Doors '95" were produced, each package that is part of the suite would define its distribution line like this:


Distribution: Doors '95

            

The icon Tag

The icon tag is used to name a file containing an icon representing the packaged software. The file may be in either GIF or XPM format, although XPM is preferred. In either case, the background of the icon should be transparent. The file should be placed in RPM's SOURCES directory prior to performing a build, so no path is needed.

The icon is normally used by graphically-oriented front ends to RPM. RPM itself doesn't use the icon, but it's stored in the package file and retained in RPM's database after the package is installed. An example icon tag might look like:


Icon: foo.xpm

            

The vendor Tag

The vendor tag is used to define the name of the entity that is responsible for packaging the software. Normally, this would be the name of an organization. Here's an example:


Vendor: White Socks Software, Inc.

            

The url Tag

The url tag is used to define a Uniform Resource Locator that can be used to obtain additional information about the packaged software. At present, RPM doesn't actively make use of this tag. The data is stored in the package however, and will be written into RPM's database when the package is installed. It's only a matter of time before some web-based RPM adjunct makes use of this information, so make sure you include URLs! Something like this is all you'll need:


URL: http://www.gnomovision.com/cdplayer.html

            

The group Tag

The group tag is used to group packages together by the types of functionality they provide. The group specification looks like a path and is similar in function, in that it specifies more general groupings before more detailed ones. For example, a package containing a text editor might have the following group:


Group: Applications/Editors

            

In this example, the package is part of the Editors group, which is itself a part of the Applications group. Likewise, a spreadsheet package might have this group:


Group: Applications/Spreadsheets

            

This group tag indicates that under the Applications group, we would find Editors and Spreadsheets, and probably some other subgroups as well.

How is this information used? It's primarily meant to permit graphical front-ends to RPM, to display packages in a hierarchical fashion. Of course, in order for groups to be as effective as possible, it's necessary for all package builders to be consistent in their groupings. In the case of packages for Linux, Red Hat has the definitive list. Therefore, Linux package builders should give serious consideration to using Red Hat's groups. The current group hierarchy is installed with every copy of RPM, and is available in the RPM sources as well. Check out the file groups in RPM's documentation directory (normally /usr/share/doc/rpm-<version>), or in the top-level source directory.

The packager Tag

The packager tag is used to hold the name and contact information for the person or persons who built the package. Normally, this would be the person that actually built the package, or in a larger organization, a public relations contact. In either case, contact information such as an e-mail address or phone number should be included, so customers can send either money or hate mail, depending on their satisfaction with the packaged software. Here's an example of a packager tag:


Packager: Fred Foonly <fred@gnomovision.com>

            

Dependency Tags

One RPM feature that's been recently implemented is a means of ensuring that if a package is installed, the system environment has everything the package requires in order to operate properly. Likewise, when an installed package is erased RPM can make sure no other package relies on the package being erased. This dependency capability can be very helpful when end users install and erase packages on their own. It makes it more difficult for them to paint themselves into a corner, package-wise.

However, in order for RPM to be able to take more than basic dependency information into account, the package builder must add the appropriate dependency information to the package. This is done by using the following tags. Note, however, that adding dependency information to a package requires some forethought. For additional information on RPM's dependency processing, please review Chapter 14, Adding Dependency Information to a Package.

The provides Tag

The provides tag is used to specify a virtual package that the packaged software makes available when it is installed. Normally, this tag would be used when different packages provide equivalent services. For example, any package that allows a user to read mail might provide the mail-reader virtual package. Another package that depends on a mail reader of some sort, could require the mail-reader virtual package. It would then install without dependency problems, if any one of several mail programs were installed. Here's what a provides tag might look like:


Provides: mail-reader

            

The requires Tag

The requires tag is used to alert RPM to the fact that the package needs to have certain capabilities available in order to operate properly. These capabilities refer to the name of another package, or to a virtual package provided by one or more packages that use the provides tag. When the requires tag references a package name, version comparisons may also be included by following the package name with <, >, =, >=, or <=, and a version specification. To get even more specific, a package's release may be included as well. Here's a requires tag in action, with a specific version requirement:


Requires: playmidi = 2.3

            

If the Requires tag needs to perform a comparison against an epoch number defined with the epoch tag (described below), then the proper format would be:


Requires: playmidi >= 4:2.3

            

The conflicts Tag

The conflicts tag is the logical complement to the requires tag. The requires tag is used to specify what packages must be present in order for the current package to operate properly. The conflicts tag is used to specify what packages cannot be installed if the current package is to operate properly.

The conflicts tag has the same format as the requires tag — namely, the tag is followed by a real or virtual package name. Like requires, the conflicts tag also accepts version and release specifications:


Conflicts: playmidi = 2.3-1

            

If the conflicts tag needs to perform a comparison against an epoch number defined with the epoch tag (described below), then the proper format would be:


Conflicts: playmidi = 4:

            

The epoch Tag

The epoch tag is another part of RPM's dependency and upgrade processing. It is also known as the serial tag. The need for it is somewhat obscure, but goes something like this:

  1. The package being built (call it package A) uses a version numbering scheme sufficiently obscure so that RPM cannot determine if one version is older or newer than another version.

  2. Another package (package B) requires that package A be installed. More specifically, it requires RPM to compare package A's version against a specified minimum (or maximum) version.

Since RPM is unable to compare package A's version against the version specified by package B, there is no way to determine if package B's dependency requirements can be met. What to do?

The epoch tag provides a way to get around this tricky problem. By specifying a simple integer epoch number for each version, you are, in essence, directing how RPM interprets the relative age of the package. The key point to keep in mind is that in order for this to work, a unique epoch number must be defined for each version of the software being packaged. In addition, the epoch number must increment along with the version. Finally, the package that requires the epoched software needs to specify its version requirements in terms of the epoch number.

Does it sound like a lot of trouble? You're right! If you find yourself in the position of needing to use this tag, take a deep breath and seriously consider changing the way you assign version numbers. If you're packaging someone else's software, perhaps you can convince them to make the change. Chances are, if RPM can't figure out the version number, most people can't, either! An example epoch tag would look something like this:


Epoch: 4

            

Note that RPM considers a package with an epoch number as newer than a package without an epoch number.

The autoreqprov, autoreq, and autoprov Tags

The autoreqprov, autoreq, and autoprov tags are used to control the automatic dependency processing performed when the package is being built. Normally, as each package is built, the following steps are performed:

  • All executable programs and libraries being packaged are analyzed to determine their shared library requirements as well as interpreters. These requirements are automatically added to the package's requirements.

  • The soname of each shared library being packaged is automatically added to the package's list of "provides" information.

  • The required modules for all Perl scripts and modules being packaged are automatically added to the package's requirements.

By doing this, RPM reduces the need for package builders to manually add dependency information to their packages. However, there are times when RPM's automatic dependency processing may not be desirable. In those cases the autoreqprov, autoreq, and autoprov tags can be used to disable automatic dependency processing altogether (autoreqprov), for requirements only (autoreq), or for "provides" only (autoprov).

To disable automatic dependency processing both for requirements and "provides", add the following line:


AutoReqProv: no

            

To disable automatic processing of requirements, add the following line:


AutoReq: no

            

To disable automatic processing of "provides", add the following line:


AutoProv: no

            

(The number zero may be used instead of no) Although RPM defaults to performing automatic dependency processing, the effect of the autoreqprov, autoreq, and autoprov tags can be reversed by changing no to yes. (The number one may be used instead of yes)

Architecture- and Operating System-Specific Tags

As RPM gains in popularity, more people are putting it to work on different types of computer systems. While this would not normally be a problem, things start to get a little tricky when one of the following two situations becomes commonplace:

  1. A particular operating system is ported to several different hardware platforms, or architectures.

  2. A particular architecture runs several different operating systems.

The real bind hits when RPM is used to package software for several of these different system environments. Without methods of controlling the build process based on architecture and operating system, package builders that develop software for more than one architecture or operating system will have a hard time indeed. The only alternative would be to maintain parallel RPM build environments and accept all the coordination headaches that would entail.

Fortunately, RPM makes it all easier than that. With the following tags, it's possible to support package building under multiple environments, all from a single set of sources, patches, and a single spec file. For a more complete discussion of multi-architecture package building, please see Chapter 19, Building Packages for Multiple Architectures and Operating Systems .

The excludearch Tag

The excludearch tag directs RPM to ensure that the package does not attempt to build on the excluded architecture(s). The reasons for preventing a package from building on a certain architecture might include:

  • The software has not yet been ported to the excluded architecture.

  • The software would serve no purpose on the excluded architecture.

An example of the first case might be that the software was designed based on the assumption that an integer is a 32-bit quantity. Obviously, this assumption is not valid on a 64-bit processor.

In the second case, software that depended on or manipulated low-level features of a given architecture, should be excluded from building on a different architecture. Assembly language programs would fall into this category.

One or more architectures may be specified after the excludearch tag, separated by either spaces or commas. Here is an example:


ExcludeArch: sparc alpha

            

In this example, RPM would not attempt to build the package on either the Sun SPARC or Digital Alpha/AXP architectures. The package would build on any other architectures, however. If a build is attempted on an excluded architecture, the following message will be displayed, and the build will fail:

# rpmbuild -ba cdplayer-1.0.spec

Arch mismatch!
cdplayer-1.0.spec doesn't build on this architecture

#
            

Note that if your goal is to ensure that a package will only build on one architecture, then you should use the exclusivearch tag.

The exclusivearch Tag

The exclusivearch tag is used to direct RPM to ensure the package is only built on the specified architecture(s). The reasons for this are similar to the those mentioned in the section on the excludearch tag above. However, the exclusivearch tag is useful when the package builder needs to ensure that only the specified architectures will build the package. This tag ensures that no future architectures will mistakenly attempt to build the package. This would not be the case if the excludearch tag were used to specify every architecture known at the time the package is built.

The syntax of the exclusivearch tag is identical to that of excludearch:


ExclusiveArch: sparc alpha

            

In this example, the package will only build on a Sun SPARC or Digital Alpha/AXP system.

Note that if your goal is to ensure that a package will not build on specific architectures, then you should use the excludearch tag.

The excludeos Tag

The excludeos tag is used to direct RPM to ensure that the package does not attempt to build on the excluded operating system(s). This is usually necessary when a package is to be built on more than one operating system, but it is necessary to keep a particular operating system from attempting a build.

Note that if your goal is to ensure that a package will only build on one operating system, then you should use the exclusiveos tag. Here's a sample excludeos tag:


ExcludeOS: linux irix

            

The exclusiveos Tag

The exclusiveos tag has the same syntax as excludeos, but it has the opposite logic. The exclusiveos tag is used to denote which operating system(s) should only be be permitted to build the package. Here's exclusiveos in action:


ExclusiveOS: linux

            

Note that if your goal is to ensure that a package will not build on a specific operating system, then you should use the excludeos tag.

Directory-related Tags

A number of tags are used to specify directories and paths that are used in various phases of RPM's build and install processes. There's not much more to say collectively about these tags, so let's dive right in and look them over.

The prefix Tag

The prefix tag is used when a relocatable package is to be built. A relocatable package can be installed normally or can be installed in a user-specified directory, by using RPM's --prefix install-time option. The data specified after the prefix tag should be the part of the package's path that may be changed during installation. For example, if the following prefix line was included in a spec file:


Prefix: /opt

            

and the following file was specified in the spec file's %files list:


/opt/blather/foonly

            

then the file foonly would be installed in /opt/blather if the package was installed normally. It would be installed in /usr/local/blather if the package was installed with the --prefix /usr/local option.

For more information about creating relocatable packages, see Chapter 15, Making a Relocatable Package.

The buildroot Tag

The buildroot tag is used to define an alternate build root. The name is a bit misleading, as the build root is actually used when the software is installed during the build process. In order for a build root to be defined and actually used, a number of issues must be taken into account. These issues are covered in Chapter 16, Making a Package That Can Build Anywhere. This is what a buildroot tag would look like:


BuildRoot: /tmp/cdplayer

            

The buildroot tag can be overridden at build-time by using the --buildroot command-line option.

Source and Patch Tags

In order to build and package software, RPM needs to know where to find the original sources. But it's not quite that simple. There might be more than one set of sources that need to be part of a particular build. In some cases, it might be necessary to prevent some sources from being packaged.

And then there is the matter of patches. It's likely that changes will need to be made to the sources, so it's necessary to specify a patch file. But the same issues that apply to source specifications are also applicable to patches. There might be more than one set of patches required.

The tags that follow are crucial to RPM, so it pays to have a firm grasp of how they are used.

The source Tag

The source tag is central to nearly every spec file. Although it has only one piece of data associated with it, it actually performs two functions:

  1. It shows where the software's developer has made the original sources available.

  2. It gives RPM the name of the original source file.

While there is no hard and fast rule, for the first function, it's generally considered best to put this information in the form of a Uniform Resource Locator (URL). The URL should point directly to the source file itself. This is due to the source tag's second function.

As mentioned above, the source tag also needs to direct RPM to the source file on the build system. How does it do this? There's only one requirement, and it is ironclad: The source filename must be at the end of the line as the final element in a path. Here's an example:


Source: ftp://ftp.gnomovision.com/pub/cdplayer-1.0.tgz

            

Given this source line, RPM will search its SOURCES directory for cdplayer-1.0.tgz. Everything prior to the filename is ignored by RPM. It's there strictly for any interested humans.

A spec file may contain more than one source tag. This is necessary for those cases where the software being packaged is contained in more than one source file. However, the source tags must be uniquely identified. This is done by appending a number to the end of the tag itself. In fact, RPM does this internally for the first source tag in a spec file, in essence turning it into source0. Therefore, if a package contains two source files, they may either be specified as:


Source: blather-4.5.tar.gz
Source1: bother-1.2.tar.gz

            

or as:


Source0: blather-4.5.tar.gz
Source1: bother-1.2.tar.gz

            

Either approach may be used. The choice is yours.

The nosource Tag

The nosource tag is used to direct RPM to omit one or more source files from the source package. Why would someone want to go to the trouble of specifying a source file, only to exclude it? The reasons for this can be varied, but let's look at one example: The software known as Pretty Good Privacy, or PGP.

PGP contains encryption routines of such high quality that the United States government restricts their export. [44] While it would be nice to create a PGP package file, the resulting package could not legally be transferred between the U.S. and other countries, or vice-versa.

However, what if all files other than the original source, were packaged using RPM? Well, a binary package made without PGP would be of little use, but what about the source package? It would contain the spec file, maybe some patches, and perhaps even an icon file. Since the controversial PGP software was not a part of the source package, this sanitized source package could be downloaded legally in any country. The person that downloaded a copy could then go about legally obtaining the PGP sources themselves, place them in RPM's SOURCES directory, and create a binary package. They wouldn't even need to change the nosource tag. One rpmbuild -ba command later, and the user would have a perfectly usable PGP binary package file.

Since there may be more than one source tag in a spec file, the format of the nosource tag is as follows:


nosource: <src-num>, <src-num><src-num>

            

The <src-num> represents the number following the source tag. If there is more than one number in the list, they may be separated by either commas or spaces. For example, consider a package containing the following source tags:


source: blather-4.5.tar.gz
Source1: bother-1.2.tar.gz
source2: blather-lib-4.5.tar.gz
source3: bother-lib-1.2.tar.gz

            

If the source files for blather and blather-lib were not to be included in the package, the following nosource line could be added:


NoSource: 0, 3

            

What about that 0? Keep in mind that the first unnumbered source tag in a spec file is automatically numbered 0 by RPM.

The patch Tag

The patch tag is used to identify which patches are associated with the software being packaged. The patch files are kept in RPM's SOURCES directory, so only the name of the patch file should be specified. Here is an example:


Patch: cdp-0.33-fsstnd.patch

            

There are no hard and fast requirements for naming the patch files, but traditionally the filename starts with the software name and version, separated by dashes. The next part of the patch file name usually includes one or more words indicating the reason for the patch. In our example above, the patch file contains changes necessary to bring the software into compliance with the Linux File System Standard, hence the fsstnd magic incantation.

RPM processes patch tags the same way it does source tags. Therefore, it's acceptable to use a Uniform Resource Locator (URL) on a patch line, too.

A spec file may contain more than one patch tag. This is necessary for those cases where the software being packaged requires more than one patch. However, the patch tags must be uniquely identified. This is done by appending a number to the end of the tag itself. In fact, RPM does this internally for the first patch tag in a spec file, in essence turning it into patch0. Therefore, if a package contains three patches, the following two methods of specifying them are equivalent:


Patch: blather-4.5-bugfix.patch
Patch1: blather-4.5-config.patch
Patch2: blather-4.5-somethingelse.patch

            

This is the same as:


Patch0: blather-4.5-bugfix.patch
Patch1: blather-4.5-config.patch
Patch2: blather-4.5-somethingelse.patch

            

Either approach may be used, but the second method looks nicer.

The nopatch Tag

The nopatch tag is similar to the nosource tag discussed earlier. Just like the nosource tag, the nopatch tag is used to direct RPM to omit something from the source package. In the case of nosource, that "something" was one or more sources. For the nopatch tag, the "something" is one or more patches.

Since each patch tag in a spec file must be numbered, the nopatch tag uses those numbers to specify which patches are not to be included in the package. The nopatch tag is used in this manner:


NoPatch: 2 3

            

In this example, the source files specified on the source2 and source3 lines are not to be included in the build.

This concludes our study of RPM's tags. In the next section, we'll look at the various scripts that RPM uses to build, as well as to install, and erase, packages.

Scripts: RPM's Workhorse

The scripts that RPM uses to control the build process are among the most varied and interesting parts of the spec file. Many spec files also contain scripts that perform a variety of tasks whenever the package is installed or erased.

The start of each script is denoted by a keyword. For example, the %build keyword marks the start of the script RPM will execute when building the software to be packaged. It should be noted that, in the strictest sense of the word, these parts of the spec file are not scripts. For example, they do not start with the traditional invocation of a shell. However, the contents of each script section are copied into a file and executed by RPM as a full-fledged script. This is part of the power of RPM: Anything that can be done in a script can be done by RPM.

Let's start by looking at the scripts used during the build process.

Build-time Scripts

The scripts that RPM uses during the building of a package follow the steps known to every software developer:

  • Unpacking the sources.

  • Building the software.

  • Installing the software.

  • Cleaning up.

Although each of the scripts perform a specific function in the build process, they share a common environment. Using RPM's --test option [45] , we can see the common portion of each script. In the following example, we've taken the cdplayer package, issued an rpmbuild -ba --test cdplayer-1.0-1.spec, and viewed the script files left in RPM's temporary directory. This section (with the appropriate package-specific values) is present in every script RPM executes during a build:


#!/bin/sh -e
# Script generated by rpm

RPM_SOURCE_DIR="/usr/src/redhat/SOURCES"
RPM_BUILD_DIR="/usr/src/redhat/BUILD"
RPM_DOC_DIR="/usr/doc"
RPM_OPT_FLAGS="-O2 -m486 -fno-strength-reduce"
RPM_ARCH="i386"
RPM_OS="Linux"
RPM_ROOT_DIR="/tmp/cdplayer"
RPM_BUILD_ROOT="/tmp/cdplayer"
RPM_PACKAGE_NAME="cdplayer"
RPM_PACKAGE_VERSION="1.0"
RPM_PACKAGE_RELEASE="1"
set -x

umask 022

          

As we can see, the script starts with the usual invocation of a shell (in this case, the Bourne shell). There are no arguments passed to the script. Next, a number of environment variables are set. Here's a brief description of each one:

  • RPM_SOURCE_DIR — This environment variable gets its value from the rpmrc file entry sourcedir, which in turn can get part of its value from the topdir entry. It is the path RPM will prepend to the file, specified in the source tag line.

  • RPM_BUILD_DIR — This variable is based on the builddir rpmrc file entry, which in turn can get part of its value from the topdir entry. This environment variable translates to the path of RPM's build directory, where most software will be unpacked and built.

  • RPM_DOC_DIR — The value of this environment variable is based on the defaultdocdir rpmrc file entry. Files marked with the %doc directive can be installed in a subdirectory of defaultdocdir. For more information on the %doc directive, refer to the section called “The %doc Directive”.

  • RPM_OPT_FLAGS — This environment variable gets its value from the optflags rpmrc file entry. It contains options that can be passed on to the build procedures of the software being packaged. Normally this means either a configuration script or the make command itself.

  • RPM_ARCH — As one might infer from the example above, this environment variable contains a string describing the build system's architecture.

  • RPM_OS — This one contains the name of the build system's operating system.

  • RPM_BUILD_ROOT — This environment variable is used to hold the "build root", into which the newly built software will be installed. If no explicit build root has been specified (either by command line option, spec file tag line, or rpmrc file entry), this variable will be null.

  • RPM_PACKAGE_NAME — This environment variable gets its value from the name tag line in the package's spec file. It contains the name of the software being packaged.

  • RPM_PACKAGE_VERSION — The version tag line is the source of this variable's translation. Predictably, this environment variable contains the software's version number.

  • RPM_PACKAGE_RELEASE — This environment variable contains the package's release number. Its value is obtained from the release tag line in the spec file.

All of these environment variables are set for your use, to make it easier to write scripts that will do the right thing even if the build environment changes.

The script also sets an option that causes the shell to print out each command, complete with expanded arguments. Finally, the default permissions are set. Past this point, the scripts differ. Let's look at the scripts in the order they are executed.

The %prep Script

The %prep script is the first script RPM executes during a build. Prior to the %prep script, RPM has performed preliminary consistency checks, such as whether the spec file's source tag points to files that actually exist. Just prior to passing control over to the %prep script's contents, RPM changes directory into RPM's build area, which, by default, is /usr/src/redhat/BUILD.

At that point, it is the responsibility of the %prep script to:

  • Create the top-level build directory.

  • Unpack the original sources into the build directory.

  • Apply patches to the sources, if necessary.

  • Perform any other actions required to get the sources in a ready-to-build state.

The first three items on this list are common to the vast majority of all software being packaged. Because of this, RPM has two macros that greatly simplify these routine functions. More information on RPM's %setup and %patch macros can be found in the section called “Macros: Helpful Shorthand for Package Builders”.

The last item on the list can include creating directories or anything else required to get the sources in a ready-to-build state. As a result, a %prep script can range from one line invoking a single %setup macro, to many lines of tricky shell programming.

The %build Script

The %build script picks up where the %prep script left off. Once the %prep script has gotten everything ready for the build, the %build script is usually somewhat anti-climactic — normally invoking make, maybe a configuration script, and little else.

Like %prep before it, the %build script has the same assortment of environment variables to draw on. Also, like %prep, %build changes directory into the software's top-level build directory (located in RPM_BUILD_DIR, or usually called <name>-<version>).

Unlike %prep, there are no macros available for use in the %build script. The reason is simple: Either the commands required to build the software are simple (such as a single make command), or they are so unique that a macro wouldn't make it easier to write the script.

The %install Script

The environment in which the %install script executes is identical to the other scripts. Like the other scripts, the %install script's working directory is set to the software's top-level directory.

As the name implies, it is this script's responsibility to do whatever is necessary to actually install the newly built software. In most cases, this means a single make install command, or a few commands to copy files and create directories.

The %check Script

The environment in which the %check script executes is identical to the other scripts. Like the other scripts, the %check script's working directory is set to the software's top-level directory.

This script's primary function is to run the test suite of the built software to ensure that the binaries work correctly. Some typical commands to run in this script are make test or make check.

The %check script is available in RPM version 4.2 and newer. [46]

The %clean Script

The %clean script, as the name implies, is used to clean up the software's build directory tree. RPM normally does this for you, but in certain cases (most notably in those packages that use a build root) you'll need to include a %clean script.

As usual, the %clean script has the same set of environment variables as the other scripts we've covered here. Since a %clean script is normally used when the package is built in a build root, the RPM_BUILD_ROOT environment variable is particularly useful. In many cases, a simple


rm -rf $RPM_BUILD_ROOT

            

will suffice. [47]

Install/Erase-time Scripts

The other type of scripts that are present in the spec file are those that are only used when the package is either installed or erased. There are four scripts, each one meant to be executed at different times during the life of a package:

  • Before installation.

  • After installation.

  • Before erasure.

  • After erasure.

Unlike the build-time scripts, there is little in the way of environment variables for these scripts. The only environment variable available is RPM_INSTALL_PREFIX, and that is only set if the package uses an installation prefix.

Unlike the build-time scripts, there is an argument defined. The sole argument to these scripts, is a number representing the number of instances of the package currently installed on the system, after the current package has been installed or erased. Sound tricky? It really isn't. Here's an example:

Assume that a package, called blather-1.0, is being installed. No previous versions of blather have been installed. Since the software is being installed, only the %pre and %post scripts are executed. The argument passed to these scripts will be 1, since the the number of blather packages installed is 1. [48]

Continuing our example, a new version of the blather package, version 1.3, is now available. Clearly it's time to upgrade. What will the scripts' values be during the upgrade? As blather-1.3 is installing, its %pre and %post scripts will have an argument equal to 2 (1 for version 1.0 already installed, plus 1 for version 1.3 being installed). As the final part of the upgrade, it's then time to erase blather version 1.0. As the package is being removed, its %preun and %postun scripts are executed. Since there will be only one blather package (version 1.3) installed after version 1.0 is erased, the argument passed to version 1.0's scripts is 1.

To finally bring an end to this example, we've decided to erase blather 1.3. We just don't need it anymore. As the package is being erased, its %preun and %postun scripts will be executed. Since there will be no blather packages installed once the erase completes, the argument passed to the scripts is 0.

With all that said, of what possible use would this argument be? Well, it has two very interesting properties:

  1. When the first version of a package is installed, its %pre and %post scripts will be passed an argument equal to 1.

  2. When the last version of a package is erased, its %preun and %postun scripts will be passed an argument equal to 0.

Based on these properties, it's trivial to write an install-time script that can take certain actions in specific circumstances. Usually, the argument is used in the %preun or %postun scripts to perform a special task when the last instance of a package is being erased.

What is normally done during these scripts? The exact tasks may vary, but in general, the tasks are any that need to be performed at these points in the package's existence. One very common task is to run ldconfig when shared libraries are installed or removed. But that's not the only use for these scripts. It's even possible to use the scripts to perform tests to ensure the package install/erasure should proceed.

Since each of these scripts will be executing on whatever system installs the package, it's necessary to choose the script's choice of tools carefully. Unless you're sure a given program is going to be available on all the systems that could possibly install your package, you should not use it in these scripts.

The %pre Script

The %pre script executes just before the package is to be installed. It is the rare package that requires anything to be done prior to installation; none of the 350 packages that comprise Red Hat Linux Linux 4.0 make use of it.

The %post Script

The %post script executes after the package has been installed. One of the most popular reasons a %post script is needed is to run ldconfig to update the list of available shared libraries after a new one has been installed. Of course, other functions can be performed in a %post script. For example, packages that install shells use the %post script to add the shell name to /etc/shells.

If a package uses a %post script to perform some function, quite often it will include a %postun script that performs the inverse of the %post script, after the package has been removed.

The %preun Script

If there's a time when your package needs to have one last look around before the user erases it, the place to do it is in the %preun script. Anything that a package needs to do immediately prior to RPM taking any action to erase the package, can be done here.

The %postun Script

The %postun script executes after the package has been removed. It is the last chance for a package to clean up after itself. Quite often, %postun scripts are used to run ldconfig to remove newly erased shared libraries from ld.so.cache.

Verification-Time Script — The %verifyscript Script

The %verifyscript executes whenever the installed package is verified by RPM's verification command. The contents of this script is entirely up to the package builder, but in general the script should do whatever is necessary to verify the package's proper installation. Since RPM automatically verifies the existence of a package's files, along with other file attributes, the %verifyscript should concentrate on different aspects of the package's installation. For example, the script may ensure that certain configuration files contain the proper information for the package being verified:


for n in ash bsh; do
    echo -n "Looking for $n in /etc/shells... "
    if ! grep "^/bin/${n}\$" /etc/shells > /dev/null; then
        echo "missing"
        echo "${n} missing from /etc/shells" >&2
    else
        echo "found"
    fi
done

          

In this script, the config file /etc/shells, is checked to ensure that it has entries for the shells provided by this package.

It is worth noting that the script sends informational and error messages to stdout, and error messages only to stderr. Normally RPM will only display error output from a verification script; the output sent to stdout is only displayed when the verification is run in verbose mode.

Macros: Helpful Shorthand for Package Builders

RPM does not support macros in the sense of ad-hoc sequences of commands being defined as a macro and executed by simply referring to the macro name.

However, there are two parts of RPM's build process that are fairly constant from one package to another, and they are the unpacking and patching of sources. Because of this, RPM makes two macros available to simplify these tasks:

  1. The %setup macro, which is used to unpack the original sources.

  2. The %patch macro, which is used to apply patches to the original sources.

These macros are used exclusively in the %prep script; it wouldn't make sense to use them anywhere else. The use of these macros is not mandatory — It is certainly possible to write a %prep script without them. But in the vast majority of cases they make life easier for the package builder.

The %setup Macro

As we mentioned above, the %setup macro is used to unpack the original sources, in preparation for the build. In its simplest form, the macro is used with no options and gets the name of the source archive from the source tag specified earlier in the spec file. Let's look at an example. The cdplayer package has the following source tag:


Source: ftp://ftp.gnomovision.com/pub/cdplayer/cdplayer-1.0.tgz

          

and the following %prep script:


%prep
%setup

          

In this simple case, the %setup macro expands into the following commands:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

          

As we can see, the %setup macro starts by changing directory into RPM's build area and removing any cdplayer build trees from previous builds. It then uses gzip to uncompress the original source (whose name was taken from the source tag), and pipes the result to tar for unpacking. The return status of the unpacking is tested. If successful, the macro continues.

At this point, the original sources have been unpacked. The %setup macro continues by changing directory into cdplayer's top-level directory. The two cd commands are an artifact of %setup's macro expansion. Finally, %setup makes sure every file in the build tree is owned by root and has appropriate permissions set.

But that's just the simplest way that %setup can be used. There are a number of other options that can be added to accommodate different situations. Let's look at them.

-n <name> — Set Name of Build Directory

In our example above, the %setup macro simply uncompressed and unpacked the sources. In this case, the tar file containing the original sources was created such that the top-level directory was included in the tar file. The name of the top-level directory was also identical to that of the tar file, which was in <name>-<version> format.

However, this is not always the case. Quite often, the original sources unpack into a directory whose name is different than the original tar file. Since RPM assumes the directory will be called <name>-<version>, when the directory is called something else, it's necessary to use %setup's -n option. Here's an example:

Assume, for a moment, that the cdplayer sources, when unpacked, create a top-level directory named cd-player. In this case, our %setup line would look like this:


%setup -n cd-player

            

and the resulting commands would look like this:


cd /usr/src/redhat/BUILD
rm -rf cd-player
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cd-player
cd /usr/src/redhat/BUILD/cd-player
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

The results are identical to using %setup with no options, except for the fact that %setup now does a recursive delete on the directory cd-player (instead of cdplayer-1.0), and changes directory into cd-player (instead of cdplayer-1.0).

Note that all subsequent build-time scripts will change directory into the directory specified by the -n option. This makes -n unsuitable as a means of unpacking sources in directories other than the top-level build directory. In the upcoming example on the section called “ Using %setup in a Multi-source Spec File ”, we'll show a way around this restriction.

A quick word of warning: If the name specified with the -n option doesn't match the name of the directory created when the sources are unpacked, the build will stop pretty quickly, so it pays to be careful when using this option.

-c — Create Directory (and change to it) Before Unpacking

How many times have you grabbed a tar file and unpacked it, only to find that it splattered files all over your current directory? Sometimes source archives are created without a top-level directory.

As you can see from the examples so far, %setup expects the archive to create its own top-level directory. If this isn't the case, you'll need to use the -c option.

This option simply creates the directory and changes directory into it before unpacking the sources. Here's what it looks like:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
mkdir -p cdplayer-1.0
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

The only changes from using %setup with no options, are the mkdir and cd commands, prior to the commands that unpack the sources. Note that you can use the -n option along with -c, so something like %setup -c -n blather works as expected.

-D — Do Not Delete Directory Before Unpacking Sources

The -D option keeps the %setup macro from deleting the software's top-level directory. This option is handy when the sources being unpacked are to be added to an already-existing directory tree. This would be the case when more than one %setup macro is used. Here's what %setup does when the -D option is employed:


cd /usr/src/redhat/BUILD
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

As advertised, the rm prior to the tar command is gone.

-T — Do Not Perform Default Archive Unpacking

The -T option disables %setup's normal unpacking of the archive file specified on the source0 line. Here's what the resulting commands look like:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

Doesn't make much sense, does it? There's a method to this madness. We'll see the -T in action in the next section.

-b <n> — Unpack The nth Sources Before Changing Directory

The -b option is used in conjunction with the source tag. Specifically, it is used to identify which of the numbered source tags in the spec file are to be unpacked.

The -b option requires a numeric argument matching an existing source tag. If a numeric argument is not provided, the build will fail:

# rpmbuild -ba cdplayer-1.0.spec
* Package: cdplayer
Need arg to %setup -b
Build failed.
#
            

Remembering that the first source tag is implicitly numbered 0, let's see what happens when the %setup line is changed to %setup -b 0:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

That's strange. The sources were unpacked twice. It doesn't make sense, until you realize that this is why there is a -T option. Since -T disables the default source file unpacking, and -b selects a particular source file to be unpacked, the two are meant to go together, like this:


%setup -T -b 0

            

Looking at the resulting commands, we find:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

That's more like it! Let's go on to the next option.

-a <n> — Unpack The nth Sources After Changing Directory

The -a option works similarly to the -b option, except that the sources are unpacked after changing directory into the top-level build directory. Like the -b option, -a requires -T in order to prevent two sets of unpacking commands. Here are the commands that a %setup -T -a 0 line would produce:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

Note that there is no mkdir command to create the top-level directory prior to issuing a cd into it. In our example, adding the -c option will make things right:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
mkdir -p cdplayer-1.0
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

The result is the proper sequence of commands for unpacking a tar file with no top-level directory.

Using %setup in a Multi-source Spec File

If all these interrelated options seem like overkill for unpacking a single source file, you're right. The real reason for the various options is to make it easier to combine several separate source archives into a single, build-able entity. Let's see how they work in that type of environment.

For the purposes of this example, our spec file will have the following three source tags: [49]


source: source-zero.tar.gz
source1: source-one.tar.gz
source2: source-two.tar.gz

            

To unpack the first source is not hard; all that's required is to use %setup with no options:


%setup

            

This produces the following set of commands:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

If source-zero.tar.gz didn't include a top-level directory, we could have made one by adding the -c option:


%setup -c

            

which would result in:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
mkdir -p cdplayer-1.0
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

Of course, if the top-level directory did not match the package name, the -n option could have been added:


%setup -n blather

            

which results in:


cd /usr/src/redhat/BUILD
rm -rf blather
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd blather
cd /usr/src/redhat/BUILD/blather
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

or


%setup -c -n blather

            

This results in:


cd /usr/src/redhat/BUILD
rm -rf blather
mkdir -p blather
cd blather
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/blather
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

Now let's add the second source file. Things get a bit more interesting here. First, we need to identify which source tag (and therefore, which source file) we're talking about. So we need to use either the -a or -b option, depending on the characteristics of the source archive. For this example, let's say that -a is the option we want. Adding that option, plus a "1" to point to the source file specified in the source1 tag, we have:


%setup -a 1

            

Since we've already seen that using the -a or -b option results in duplicate unpacking, we need to disable the default unpacking by adding the -T option:


%setup -T -a 1

            

Next, we need to make sure that the top-level directory isn't deleted. Otherwise, the first source file we just unpacked would be gone. That means we need to include the -D option to prevent that from happening. Adding this final option, and including the now complete macro in our %prep script, we now have:


%setup
%setup -T -D -a 1

            

This will result in the following commands:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .
cd /usr/src/redhat/BUILD
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-one.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .

            

So far, so good. Let's include the last source file, but with this one, we'll say that it needs to be unpacked in a subdirectory of cdplayer-1.0 called database. Can we use %setup in this case?

We could, if source-two.tgz created the database subdirectory. If not, then it'll be necessary to do it by hand. For the purposes of our example, let's say that source-two.tgz wasn't created to include the database subdirectory, so we'll have to do it ourselves. Here's our %prep script now:


%setup
%setup -T -D -a 1
mkdir database
cd database
gzip -dc /usr/src/redhat/SOURCES/source-two.tar.gz | tar -xvvf -

            

Here's the resulting script:


cd /usr/src/redhat/BUILD
rm -rf cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-zero.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
cd cdplayer-1.0
cd /usr/src/redhat/BUILD/cdplayer-1.0
chown -R root.root .
chmod -R a+rX,g-w,o-w .
cd /usr/src/redhat/BUILD
cd cdplayer-1.0
gzip -dc /usr/src/redhat/SOURCES/source-one.tar.gz | tar -xvvf -
if [ $? -ne 0 ]; then
  exit $?
fi
mkdir database
cd database
gzip -dc /usr/src/redhat/SOURCES/source-two.tar.gz | tar -xvvf -

            

The three commands we added to unpack the last set of sources were added to the end of the %prep script.

The bottom line to using the %setup macro is that you can probably get it to do what you want, but don't be afraid to tinker. And even if %setup can't be used, it's easy enough to add the necessary commands to do the work manually. Above all, make sure you use the --test option when testing your %setup macros, so you can see what commands they're translating to.

Next, let's look at RPM's second macro, %patch.

The %patch Macro

The %patch macro, as its name implies, is used to apply patches to the unpacked sources. In the following examples, our spec file has the following patch tag lines:


patch0: patch-zero
patch1: patch-one
patch2: patch-two

          

At its simplest, the %patch macro can be invoked without any options:


%patch

          

Here are the resulting commands:


echo "Patch #0:"
patch -p0  -s < /usr/src/redhat/SOURCES/patch-zero

          

The %patch macro nicely displays a message showing that a patch is being applied, then invokes the patch command to actually do the dirty work. There are two options to the patch command:

  1. The -p option, which directs patch to remove the specified number of slashes (and any intervening directories) from the front of any filenames specified in the patch file. In this case, nothing will be removed.

  2. The -s option, which directs patch to apply the patch without displaying any informational messages. Only errors from patch will be displayed.

How did the %patch macro know which patch to apply? Keep in mind that, like the source tag lines, every patch tag is numbered, starting at zero. The %patch macro, by default, applies the patch file named on the patch (or patch0) tag line.

Specifying Which patch Tag to Use

The %patch macro actually has two different ways to specify the patch tag line it is to use. The first method is to simply append the number of the desired patch tag to the end of the %patch macro itself. For example, in order to apply the patch specified on the patch2 tag line, the following %patch macro could be used:


%patch2

            

The other approach is to use the -P option. This option is followed by the number of the patch tag line desired. Therefore, this line is identical in function to the previous one:


%patch -P 2

            

Note that the -P option will not apply the file specified on the patch0 line, by default. Therefore, if you choose to use the -P option to specify patch numbers, you'll need to use the following format when applying patch zero:


%patch -P 0

            

-p <#> — Strip <#> leading slashes and directories from patch filenames

The -p (Note the lowercase "p"!) option is sent directly to the patch command. It is followed by a number, which specifies the number of leading slashes (and the directories in between) to strip from any filenames present in the patch file. For more information on this option, please consult the patch man page.

-b <name> — Set the backup file extension to <name>

When the patch command is used to apply a patch, unmodified copies of the files patched are renamed to end with the extension .orig. The -b option is used to change the extension used by patch. This is normally done when multiple patches are to be applied to a given file. By doing this, copies of the file as it existed prior to each patch, are readily available.

-E — Remove Empty Output Files

The -E option is passed directly to the patch program. When patch is run with the -E option, any output files that are empty after the patches have been applied, are removed.

Now let's take %patch on a test-drive, and put it through its paces.

An example of the %patch Macro in Action

Using the example patch tag lines we've used throughout this section, let's put together an example and look at the resulting commands. In our example, the first patch to be applied needs to have the root directory stripped. Its %patch macro will look like this:


%patch -p1

            

The next patch is to be applied to files in the software's lib subdirectory, so we'll need to add a cd command to get us there. We'll also need to strip an additional directory:


cd lib
%patch -P 1 -p2

            

Finally, the last patch is to be applied from the software's top-level directory, so we need to cd back up a level. In addition, this patch modifies some files that were also patched the first time, so we'll need to change the backup file extension:


cd ..
%patch -P 2 -p1 -b .last-patch

            

Here's what the %prep script (minus any %setup macros) looks like:


%patch -p1
cd lib
%patch -P 1 -p2
cd ..
%patch -P 2 -p1 -b .last-patch

            

And here's what the macros expand to:


echo "Patch #0:"
patch -p1  -s < /usr/src/redhat/SOURCES/patch-zero
cd lib
echo "Patch #1:"
patch -p2  -s < /usr/src/redhat/SOURCES/patch-one
cd ..
echo "Patch #2:"
patch -p1 -b .last-patch -s < /usr/src/redhat/SOURCES/patch-two

            

No surprises here. Note that the %setup macro leaves the current working directory set to the software's top-level directory, so our cd commands with their relative paths will do the right thing. Of course, we have environment variables available that could be used here, too.

Compressed Patch Files

If a patch file is compressed with gzip, RPM will automatically decompress it before applying the patch. Here's a compressed patch file as specified in the spec file:


Patch: bother-3.5-hack.patch.gz

              

This is part of the script RPM will execute when the %prep section is executed:


echo Executing: %prep
…
echo "Patch #0:"
gzip -dc /usr/src/redhat/SOURCES/bother-3.5-hack.patch.gz | patch -p1  -s
…

              

First, the patch file is decompressed using gzip. The output from gzip is then piped into patch.

That's about it for RPM's macros. Next, let's take a look at the %files list.

The %files List

The %files list indicates to RPM which files on the build system are to be packaged. The list consists of one file per line. The file may have one or more directives preceding it. These directives give RPM additional information about the file and are discussed more fully below.

Normally, each file includes its full path. The path performs two functions. First, it specifies the file's location on the build system. Second, it denotes where the file should be placed when the package is to be installed. [50]

For packages that create directories containing hundreds of files, it can be quite cumbersome creating a list that contains every file. To make this situation a bit easier, if the %files list contains a path to a directory, RPM will automatically package every file in that directory, as well as every file in each subdirectory. Shell-style globbing can also be used in the %files list.

Directives For the %files list

The %files list may contain a number of different directives. They are used to:

  • Identify documentation and configuration files.

  • Ensure that a file has the correct permissions and ownership set.

  • Control which aspects of a file are to be checked during package verification.

  • Eliminate some of the tedium in creating the %files list.

In the %files list, one or more directives may be placed on a line, separated by spaces, before one or more filenames. Therefore, if %foo and %bar are two %files list directives, they may be applied to a file baz in the following manner:


%foo %bar baz

        

Now it's time to take a look at the directives that inhabit the %files list.

File-related Directives

RPM processes files differently according to their type. However, RPM does not have a method of automatically determining file types. Therefore, it is up to the package builder to appropriately mark files in the %files list. This is done using one of the directives below.

Keep in mind that not every file will need to be marked. As you read the following sections, you'll see that directives are only used in special circumstances. In most packages, the majority of files in the %files list will not need to be marked.

The %doc Directive

The %doc directive flags the filename(s) that follow, as being documentation. RPM keeps track of documentation files in its database, so that a user can easily find information about an installed package. In addition, RPM can create a package-specific documentation directory during installation and copy documentation into it. Whether or not this additional step is taken, is dependent on how a file is specified. Here is an example:


%doc README
%doc /usr/local/foonly/README

            

The file README exists in the software's top-level directory during the build, and is included in the package file. When the package is installed, RPM creates a directory in the documentation directory named the same as the package (ie, <software>-<version>-<release>), and copies the README file there. The newly created directory and the README file are marked in the RPM database as being documentation. The default documentation directory is /usr/doc, and can be changed by setting the defaultdocdir rpmrc file entry. For more information on rpmrc files, please see Appendix B, The rpmrc File.

The file /usr/local/foonly/README was installed into that directory during the build and is included in the package file. When the package is installed, the README file is copied into /usr/local/foonly and marked in the RPM database as being documentation.

The %config Directive

The %config directive is used to flag the specified file as being a configuration file. RPM performs additional processing for config files when packages are erased, and during installations and upgrades. This is due to the nature of config files: They are often changed by the system administrator, and those changes should not be lost.

There is a restriction to the %config directive, and that restriction is that no more than one filename may follow the %config. This means that the following example is the only allowable way to specify config files:


%config /etc/foonly

            

Note that the full path to the file, as it is installed at build time, is required.

The %attr Directive

The %attr directive permits finer control over three key file attributes:

  1. The file's permissions, or "mode".

  2. The file's user ID.

  3. The file's group ID.

The %attr directive has the following format:


%attr(<mode>, <user>, <group>) file

            

The mode is specified in the traditional numeric format, while the user and group are specified as a string, such as "root". Here's a sample %attr directive:


%attr(755, root, root) foo.bar

            

This would set foo.bar's permissions to 755. The file would be owned by user root, group root. If a particular attribute does not need to be specified (usually because the file is installed with that attribute set properly), then that attribute may be replaced with a dash:


%attr(755, -, root) foo.bar

            

The main reason to use the %attr directive is to permit users without root access to build packages. The techniques for doing this (and a more in-depth discussion of the %attr directive) can be found in Chapter 16, Making a Package That Can Build Anywhere.

The %defattr Directive

The %defattr directive allows setting of default attributes for files and directives. The %defattr has a similar format to the %attr directive:

  1. The default permissions, or "mode" for files.

  2. The default user id.

  3. The default group id.

  4. The default permissions, or "mode" for directories.

The %attr directive has the following format:


%defattr(<file mode>, <user>, <group>, <dir mode>)

            

As with %attr if a particular attribute does not need to be specified (usually because the file is installed with that attribute set properly), then that attribute may be replaced with a dash. In addition the directory mode may be ommited. %defattr tends to be used at the top of %files.

The %ghost Directive

As we mentioned in the section called “The %files List”, if a file is specified in the %files list, that file will automatically be included in the package. There are times when a file should be owned by the package but not installed - log files and state files are good examples of cases you might desire this to happen.

The way to achieve this, is to use the %ghost directive. By adding this directive to the line containing a file, RPM will know about the ghosted file, but will not add it to the package. However it still needs to be in the buildroot. Here's an example of %ghost in action.

The blather-1.0 package logs to /var/log/blather.log in it's default config. In the spec file, the /var/log/blather.log file is included in the %files list. We can see that blather.log belongs to the package, and it is removed when the package is.


%install
touch $RPM_BUILD_ROOT%{_localstatedir}/log/blather.log
…
%files
…
%ghost %{_localstatedir}/log/blather.log
…

# rpm -qf /var/log/blather.log

blather-1.0-1

# rpm -ql blather | grep blather.log



# rpm -e blather && ls /var/log/blather.log

ls: /var/log/blather.log: No such file or directory

            

There file touched in the %install stage will not be installed to /var/log/blather.log although it will be added to the rpm database, as we can see from querying the file, however it is not visible from a package listing, but as it is owned by the package it will be removed when the package is removed. In addition it is possible to use setperms to fix the permissions on a %ghost file.

# ls -al /var/log/blather.log

-rw-r--r--    1 root     root         3448 Jun 18 17:00 /var/log/blather.log

#chmod 666 /var/log/blather.log
# ls -al /var/log/blather.log

-rw-rw-rw-    1 root     root         3448 Jun 18 17:00 /var/log/blather.log

#rpm --setperms blather
# ls -al /var/log/blather.log

-rw-r--r--    1 root     root         3448 Jun 18 17:00 /var/log/blather.log

            

The %verify Directive

RPM's ability to verify the integrity of the software it has installed is impressive. But sometimes it's a bit too impressive. After all, RPM can verify as many as nine different aspects of every file. The %verify directive can control which of these file attributes are to be checked when an RPM verification is done. Here are the attributes, along with the names used by the %verify directive:

  1. Owner (owner)

  2. Group (group)

  3. Mode (mode)

  4. MD5 Checksum (md5)

  5. Size (size)

  6. Major Number (maj)

  7. Minor Number (min)

  8. Symbolic Link String (symlink)

  9. Modification Time (mtime)

How is %verify used? Say, for instance, that a package installs device files. Since the owner of a device will change, it doesn't make sense to have RPM verify the device file's owner/group and give out a false alarm. Instead, the following %verify directive could be used:


%verify(mode md5 size maj min symlink mtime) /dev/ttyS0

            

We've left out owner and group, since we'd rather RPM not verify those. [51]

However, if all you want to do is prevent RPM from verifying one or two attributes, you can use %verify's alternate syntax:


%verify(not owner group) /dev/ttyS0

            

This use of %verify produces identical results to the previous example.

Directory-related Directives

While the two directives in this section perform different functions, each is related to directories in some way. Let's see what they do:

The %docdir Directive

The %docdir directive is used to add a directory to the list of directories that will contain documentation. RPM includes the directories /usr/doc, /usr/info, and /usr/man in the %docdir list by default.

For example, if the following line is part of the %files list:


%docdir /usr/blather

            

any files in the %files list that RPM packages from /usr/blather will be included in the package as usual, but will also be automatically flagged as documentation. This directive is handy when a package creates its own documentation directory and contains a large number of files. Let's give it a try by adding the following line to our spec file:


%docdir /usr/blather

            

Our %files list contains no references to the several files the package installs in the /usr/blather directory. After building the package, looking at the package's file list shows:

# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm

#
            

Wait a minute: There's nothing there, not even /usr/blather! What happened?

The problem is that %docdir only directs RPM to mark the specified directory as holding documentation. It doesn't direct RPM to package any files in the directory. To do that, we need to clue RPM in to the fact that there are files in the directory that must be packaged.

One way to do this is to simply add the files to the %files list:


%docdir /usr/blather
/usr/blather/INSTALL

            

Looking at the package, we see that INSTALL was packaged:

# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm

…
/usr/blather/INSTALL

#
            

Directing RPM to only show the documentation files, we see that INSTALL has indeed been marked as documentation, even though the %doc directive had not been used:

# rpm -qdp ../RPMS/i386/blather-1.0-1.i386.rpm

…
/usr/blather/INSTALL

#
            

Of course, if you go to the trouble of adding each file to the %files list, it wouldn't be that much more work to add %doc to each one. So the way to get the most benefit from %docdir is to add another line to the %files list:


%docdir /usr/blather
/usr/blather

            

Since the first line directs RPM to flag any file in /usr/blather as being documentation, and the second line tells RPM to automatically package any files found in /usr/blather, every single file in there will be packaged and marked as documentation:

# rpm -qdp ../RPMS/i386/blather-1.0-1.i386.rpm

/usr/blather
/usr/blather/COPYING
/usr/blather/INSTALL
/usr/blather/README
…

#
            

The %docdir directive can save quite a bit of effort in creating the %files list. The only caveat is that you must be sure the directory will only contain files you want marked as documentation. Keep in mind, also, that all subdirectories of the %docdir'ed directory will be marked as documentation directories, too.

The %dir Directive

As we mentioned in the section called “The %files List”, if a directory is specified in the %files list, the contents of that directory, and the contents of every directory under it, will automatically be included in the package. While this feature can be handy (assuming you are sure that every file under the directory should be packaged) there are times when this could be a problem.

The way to get around this, is to use the %dir directive. By adding this directive to the line containing the directory, RPM will package only the directory itself, regardless of what files are in the directory at the time the package is created. Here's an example of %dir in action.

The blather-1.0 package creates the directory /usr/blather as part of its build. It also puts several files in that directory. In the spec file, the /usr/blather directory is included in the %files list:


%files
…
/usr/blather
…

            

There are no other entries in the %files list that have /usr/blather as part of their path. After building the package, we use RPM to look at the files in the package:

# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm

…
/usr/blather
/usr/blather/COPYING
/usr/blather/INSTALL
/usr/blather/README
…

#
            

The files present in /usr/blather at the time the package was built were included in the package automatically, without entering their names in the %files list.

However, after changing the /usr/blather line in the %files list to:


%dir /usr/blather

            

and rebuilding the package, a listing of the package's files now includes only the /usr/blather directory:

# rpm -qlp ../RPMS/i386/blather-1.0-1.i386.rpm

…
/usr/blather
…

#
            

-f <file> — Read the %files List From <file>

The -f option is used to direct RPM to read the %files list from the named file. Like the %files list in a spec file, the file named using the -f option should contain one filename per line and also include any of the directives named in this section.

Why is it necessary to read filenames from a file rather than have the filenames in the spec file? Here's a possible reason:

The filenames' paths may contain a directory name that can only be determined at build-time, such as an architecture specification. The list of files, minus the variable part of the path, can be created, and sed can be used at build-time to update the path appropriately.

It's not necessary that every filename to be packaged reside in the file. If there are any filenames present in the spec file, they will be packaged as well:


%files latex -f tetex-latex-skel
/usr/bin/latex
/usr/bin/pslatex
…

            

Here, the filenames present in the file tetex-latex-skel would be packaged, followed by every filename following the %files line.

The Lone Directive: %package

While every directive we've seen so far is used in the %files list, the %package directive is different. It is used to permit the creation of more than one package per spec file and can appear at any point in the spec file. These additional packages are known as subpackages. Subpackages are named according to the contents of the line containing the %package directive. The format of the package directive is:


%package: <string>

        

The <string> should be a name that describes the subpackage. This string is appended to the base package name to produce the subpackage's name. For example, if a spec file contains a name tag value of "foonly", and a "%package doc" line, then the subpackage name will be foonly-doc.

-n <string> — Use <string> As the Entire Subpackage Name

As we mentioned above, the name of a subpackage normally includes the main package name. When the -n option is added to the %package directive, it directs RPM to use the name specified on the %package line as the entire package name. In the example above, the following %package line would create a subpackage named foonly-doc:


%package doc

          

The following %package line would create a subpackage named doc:


%package -n doc

          

The %package directive plays another role in subpackage building. That role is to act as a place to collect tags that are specific to a given subpackage. Any tag placed after a %package directive will only apply to that subpackage.

Finally, the name string specified by the %package directive is also used to denote which parts of the spec file are a part of that subpackage. This is done by including the string (along with the -n option, if present on the %package line) on the starting line of the section that is to be subpackage-specific. Here's an example:


…
%package -n bar
…
%post -n bar
…

          

In this heavily edited spec file segment, a subpackage called bar has been defined. Later in the file is a post-install script. Because it has subpackage bar's name on the %post line, the post-install script will be part of the bar subpackage only.

For more information on building subpackages, please see Chapter 18, Creating Subpackages.

Conditionals

While the "exclude" and "exclusive" tags (excludearch, exclusivearch, excludeos, and exclusiveos) provide some control over whether a package will be built on a given architecture and/or operating system, that control is still rather coarse.

For example, what should be done if a package will build under multiple architectures, but requires slightly different %build scripts? Or what if a package requires a certain set of files under one operating system, and an entirely different set under another operating system? The architecture and operating system-specific tags we've discussed earlier in the chapter do nothing to help in such situations. What can be done?

One approach would be to simply create different spec files for each architecture or operating system. While it would certainly work, this approach has some problems:

  • More work. The existence of multiple spec files for a given package means that the effort required to make any changes to the package is multiplied by however many different spec files there are.

  • More chance for mistakes. If any work needs to be done to the spec files, the fact they are separate means it is that much easier to forget to make the necessary changes to each one. There is also the chance of introducing mistakes each time changes are made.

The other approach is to somehow permit the conditional inclusion of architecture- or operating system-specific sections of the spec file. Fortunately, the RPM designers chose this approach, and it makes multi-platform package building easier and less prone to mistakes.

We discuss multi-platform package building in depth in Chapter 19, Building Packages for Multiple Architectures and Operating Systems . For now, let's take a quick look at RPM's conditionals.

The %ifarch Conditional

The %ifarch conditional is used to begin a section of the spec file that is architecture-specific. It is followed by one or more architecture specifiers, each separated by commas or whitespace. Here is an example:


%ifarch i386 sparc

          

The contents of the spec file following this line would be processed only by Intel x86 or Sun SPARC-based systems. However, if only this line were placed in a spec file, this is what would happen if a build was attempted:

# rpmbuild -ba cdplayer-1.0.spec

Unclosed %if
Build failed.

#
          

The problem that surfaced here is that any conditional must be "closed" by using either %else or %endif. We'll be covering them a bit later in the chapter.

The %ifnarch Conditional

The %ifnarch conditional is used in a similar fashion to %ifarch, except that the logic is reversed. If a spec file contains a conditional block starting with %ifarch alpha, that block would be processed only if the build was being done on a Digital Alpha/AXP-based system. However, if the conditional block started with %ifnarch alpha, then that block would be processed only if the build were not being done on an Alpha.

Like %ifarch, %ifnarch can be followed by one or more architectures and must be closed by a %else or %endif.

The %ifos Conditional

The %ifos conditional is used to control RPM's spec file processing based on the build system's operating system. It is followed by one or more operating system names. A conditional block started with %ifos must be closed by a %else or %endif. Here's an example:


%ifos linux

          

The contents of the spec file following this line would be processed only if the build was done on a linux system.

The %ifnos Conditional

The %ifnos conditional is the logical complement to %ifos: that is, if a conditional starting with the line %ifnos irix is present in a spec file, then the file contents after the %ifnos will not be processed if the build system is running Irix. As always, a conditional block starting with %ifnos must be closed by a %else or %endif.

The %else Conditional

The %else conditional is placed between a %if conditional of some persuasion, and a %endif. It is used to create two blocks of spec file statements, only one of which will be used in any given case. Here's an example:


%ifarch alpha
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS -I ."
%else
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS"
%endif

          

When a build is performed on a Digital Alpha/AXP, some additional flags are added to the make command. On all other systems, these flags are not added.

The %endif Conditional

A %endif is used to end a conditional block of spec file statements. It can follow one of the %if conditionals, or the %else. The %endif is always needed after a conditional, otherwise the build will fail. Here's short conditional block, ending with a %endif:


%ifarch i386
make INTELFLAG=-DINTEL
%endif

          

In this example, we see the conditional block started with a %ifarch and ended with a %endif.

Now that we have some more in-depth knowledge of the spec file, let's take a look at some of RPM's additional features. In the next chapter, we'll explore how to add dependency information to a package.



[44] There is also an "international" version that may be used in non-US countries. See Appendix G, An Introduction to PGP.

[46] One popular hack to make spec files containing the %check script "work" with RPM versions older than 4.2 roughly similarly as in newer versions is to include it immediately after the %install script in the spec file and append "|| :" to it, like:


%check || :

                

[47] Keep in mind that this command in a %clean script can wreak havoc if used with a build root of, say, /. the section called “Using --buildroot Can Bite You!” discusses this in more detail.

[48] Or it will be 1, once the package is completely installed. Remember, the number is based on the number of packages installed after the current package's install or erase has completed.

[49] Yes, the source tags should include a URL pointing to the sources.

[50] This is not entirely the case when a relocatable package is being built. For more information on relocatable packages, see Chapter 15, Making a Relocatable Package.

[51] RPM will automatically exclude file attributes from verification if it doesn't make sense for the type of file. In our example, getting the MD5 checksum of a device file is an example of such a situation.

Chapter 14. Adding Dependency Information to a Package

Since the very first version of RPM hit the streets, one of the side effects of RPM's ease of use was that it made it easier for people to break things. Since RPM made it so simple to erase packages, it became common for people to joyfully erase packages until something broke.

Usually this only bit people once, but even once was too much of a hassle if it could be prevented. With this in mind, the RPM developers gave RPM the ability to:

  • Build packages that contain information on the capabilities they require.

  • Build packages that contain information on the capabilities they provide.

  • Store this "provides" and "requires" information in the RPM database.

In addition, they made sure RPM was able to display dependency information, as well as to warn users if they were attempting to do something that would break a package's dependency requirements.

With these features in place, it became more difficult for someone to unknowingly erase a package and wreak havoc on their system.

An Overview of Dependencies

We've already alluded to the underlying concept for RPM's dependency processing. It is based on two key factors:

  • Packages advertise what capabilities they provide.

  • Packages advertise what capabilities they require.

By simply checking these two types of information, many possible problems can be avoided. For example, if a package requires a capability that is not provided by any already-installed package, that package cannot be installed and expected to work properly.

On the other hand, if a package is to be erased, but its capabilities are required by other installed packages, then it cannot be erased without causing other packages to fail.

As you might imagine, it's not quite that simple. But adding dependency information can be easy. In fact, in most cases, it's automatic!

Automatic Dependencies

When a package is built by RPM, if any file in the package's %files list is a shared library, the library's soname is automatically added to the list of capabilities the package provides. The soname is the name used to determine compatibility between different versions of a library.

Note that this is not a filename. In fact, no aspect of RPM's dependency processing is based on filenames. Many people new to RPM often make the assumption that a failed dependency represents a missing file. This is not the case.

Remember that RPM's dependency processing is based on knowing what capabilities are provided by a package and what capabilities a package requires. We've seen how RPM automatically determines what shared library resources a package provides. But does it automatically determine what shared libraries a package requires?

Yes! RPM does this by running ldd on every executable program in a package's %files list. Since ldd provides a list of the shared libraries each program requires, both halves of the equation are complete — that is, the packages that make shared libraries available, and the packages that require those shared libraries, are tracked by RPM. RPM can then take that information into account when packages are installed, upgraded, or erased.

The Automatic Dependency Scripts

RPM uses two scripts to handle automatic dependency processing. They reside in /usr/bin and are called find-requires, and find-provides. We'll take a look at them in a minute, but first let's look at why there are scripts to do this sort of thing. Wouldn't it be better to have this built into RPM itself?

Actually, creating scripts for this sort of thing is a better idea. The reason? RPM has already been ported to a variety of different operating systems. Determining what shared libraries an executable requires, and the soname of shared libraries, is simple, but the exact steps required vary widely from one operating system to another. Putting this part of RPM into a script makes it easier to port RPM.

Let's take a look at the scripts that are used by RPM under the Linux operating system.

find-requires — Automatically Determine Shared Library Requirements

The find-requires script for Linux is quite simple:


#!/bin/sh

# note this works for both a.out and ELF executables

ulimit -c 0

filelist=`xargs -r file | fgrep executable | cut -d: -f1 `

for f in $filelist; do
    ldd $f | awk '/=>/ { print $1 }'
done | sort -u | xargs -r -n 1 basename | sort -u

            

This script first creates a list of executable files. Then, for each file in the list, ldd determines the file's shared library requirements, producing a list of sonames. Finally, the list of sonames is sanitized by removing duplicates, and removing any paths.

find-provides — Automatically Determine Shared Library Sonames

The find-provides script for Linux is a bit more complex, but still pretty straightforward:


#!/bin/bash

# This script reads filenames from STDIN and outputs any relevant
# provides information that needs to be included in the package.

filelist=$(grep ".so" | grep -v "^/lib/ld.so" | 
xargs file -L 2>/dev/null | grep "ELF.*shared object" | cut -d: -f1)

for f in $filelist; do
    soname=$(objdump -p $f | awk '/SONAME/ {print $2}')

    if [ "$soname" != "" ]; then
        if [ ! -L $f ]; then
            echo $soname
        fi
    else
        echo ${f##*/}
    fi
done | sort -u

            

First, a list of shared libraries is created. Then, for each file on the list, the soname is extracted, cleaned up, and duplicates removed.

Automatic Dependencies: An Example

Let's take a widely used program, ls, the directory lister, as an example. On a Red Hat Linux system, ls is part of the fileutils package and is installed in /bin. Let's play the part of RPM during fileutils' package build and run find-requires on /bin/ls. Here's what we'll see:

# find-requires
/bin/ls
<ctrl-d>

libc.so.5

#
          

The find-requires script returned libc.so.5. Therefore, RPM should add a requirement for libc.so.5 when the fileutils package is built. We can verify that RPM did add ls' requirement for libc.so.5 by using RPM's --requires option to display fileutils' requirements:

# rpm -q --requires fileutils

libc.so.5

#
          

OK, that's the first half of the equation — RPM automatically detecting a package's shared library requirements. Now let's look at the second half of the equation -- RPM detecting packages that provide shared libraries. Since the libc package includes, among others, the shared library /lib/libc.so.5.3.12, RPM would obtain its soname. We can simulate this by using find-provides to print out the library's soname:

# find-provides
/lib/libc.so.5.3.12
Ctrl-D

libc.so.5

#
          

OK, so /lib/libc.so.5.3.12's soname is libc.so.5. Let's see if the libc package really does "provide" the libc.so.5 soname:

# rpm -q --provides libc

libm.so.5
libc.so.5

#
          

Yes, there it is, along with the soname of another library contained in the package. In this way, RPM can ensure that any package requiring libc.so.5 will have a compatible library available as long as the libc package, which provides libc.so.5, is installed.

In most cases, automatic dependencies are enough to fill the bill. However, there are circumstances when the package builder has to manually add dependency information to a package. Fortunately, RPM's approach to manual dependencies is both simple and flexible.

The autoreqprov, autoreq, and autoprov Tags — Disable Automatic Dependency Processing

There may be times when RPM's automatic dependency processing is not desired. In these cases, the autoreqprov, autoreq, and autoprov tags may be used to disable it. This tag takes a yes/no or 0/1 value. For example, to disable automatic dependency processing, the following line may be used:


AutoReqProv: no

          

The autoreq and autoprov tags can be used to disable automatic processing of requirements or "provides" only, respectively.

Manual Dependencies

You might have noticed that we've been using the words "requires" and "provides" to describe the dependency relationships between packages. As it turns out, these are the exact words used in spec files to manually add dependency information. Let's look at the first tag: Requires.

The Requires Tag

We've been deliberately vague when discussing exactly what it is that a package requires. Although we've used the word "capabilities", in fact, manual dependency requirements are always represented in terms of packages. For example, if package foo requires that package bar is installed, it's only necessary to add the following line to foo's spec file:


Requires: bar

          

Later, when the foo package is being installed, RPM will consider foo's dependency requirements met if any version of package bar is already installed. [52]

If more than one package is required, they can be added to the Requires tag, one after another, separated by commas and/or spaces. So if package foo requires packages bar and baz, the following line will do the trick:


Requires: bar, baz

          

As long as any version of bar and baz is installed, foo's dependencies will be met.

Adding Version Requirements

When a package has slightly more stringent needs, it's possible to require certain versions of a package. All that's necessary is to add the desired version number, preceded by one of the following comparison operators:

  • Requires package with a version less than the specified version.

  • Requires package with a version less than or equal to the specified version.

  • Requires package with a version equal to the specified version.

  • Requires package with a version equal to or greater than the specified version.

  • Requires package with a version greater than the specified version.

Continuing with our example, let's suppose that the required version of package bar actually needs to be at least 2.7, and that the baz package must be version 2.1 — no other version will do. Here's what the Requires tag line would look like:


Requires: bar >= 2.7, baz = 2.1

            

We can get even more specific and require a particular release of a package:


Requires: bar >= 2.7-4, baz = 2.1-1

            

When Version Numbers Aren't Enough

You might think that with all these features, RPM's dependency processing can handle every conceivable situation. You'd be right, except for the problem of version numbers. RPM needs to be able to determine which version numbers are more recent than others, in order to perform its version comparisons.

It's pretty simple to determine that version 1.5 is older than version 1.6. But what about 2.01 and 2.1? Or 7.6a and 7.6? There's no way for RPM to keep up with all the different version-numbering schemes in use. But there is a solution; two, in fact…

Solution Number 1: Epoch numbers

When RPM can't decipher a package's version number, it's time to pull out the Epoch tag. This tag is used to help RPM determine version number ordering. Here's a sample Epoch tag line:


Epoch: 42

              

This line indicates that the package has an epoch number of 42. What does the 42 mean? Only that this version of the package is newer than the same package with an epoch number of 41, but older than the same package with an epoch number of 43. If you think of epoch numbers as being nothing more than very simple version numbers, you'll be on the mark. In other words, Epoch is the most significant component of a package's complete version identifier with regards to RPM's version comparison algorithm.

In order to direct RPM to look at the epoch number instead of the version number when doing dependency checking, it's necessary to use a ":" before the version in the Requires tag line. So if a package requires package foo to have an epoch number equal to 42, the following tag line would be used:


Requires: foo = 42:

              

If the foo package needs to have an epoch number greater than or equal to 42, this line would work:


Requires: foo >= 42:

              

If the foo package needs to have version with an epoch number 42 and version 1.0, this line would work:


Requires: foo >= 42:1.0

              

You must include the epoch in a requires if it exists in the package.

It might seem that using epoch numbers is a lot of extra trouble, and you're right. But there is an alternative:

Solution Number 2: Just Say No!

If you have the option between changing the software's version-numbering scheme, or using epoch numbers in RPM, please consider changing the version-numbering scheme. Chances are, if RPM can't figure it out, most of the people using your software can't, either. But in case you aren't the author of the software you're packaging, and its version numbering scheme is giving RPM fits, the epoch tag can help you out.

Fine Grained Dependencies

For the vast majority of dependencies, using the normal Requires is enough. However, there are some special situations where one might want more fine grained control over them. When multiple packages are being installed in a transaction, installation order and dependency loops are such cases. Erasure order of packages within a transaction is the opposite of their installation order.

A very trivial example of a dependency loop is when package foo requires bar, and bar requires foo. However, when the number of packages involved in a loop grows, the loops get more and more complex. The special dependency types in this chapter are at best hints</