Working with source RPMs under CentOS.

Printer-friendly versionPrinter-friendly version

What's happening here?

Since the online documentation is somewhat hard to come by, this is a short tutorial on how to download and work with source RPM files in CentOS 5.5. And by "work with," I don't necessarily mean compiling and installing; you might simply be interested in examining the source code for one of your CentOS packages, and we'll show you how to do just that.

The essential caveat

As a number of online pages will caution you, you should be extremely wary of downloading, building and installing your own packages on a stable CentOS system since you're effectively "breaking" the standard configuration.

However, as long as you're merely interested in, say, poking around the source of a package, this is a perfectly safe operation and can, in fact, be done purely as a non-root user, guaranteeing that you can't break anything.

The pre-requisite packages

If you're at the point where you're interested in working with package source, chances are you're interested in development, so you can either install the required development packages one by one, or just go for the gusto:

# yum groupinstall "Development Tools"

That should give you all of the packages you need for the rest of this tutorial, other than the package that provides the yumdownloader utility we'll be needing later:

# yum install yum-utils

And we're almost ready to go.

The example

Just for fun, we're going to examine the source code for the lsmod command which, as you can see, is part of the module-init-tools package:

$ rpm -qf /sbin/lsmod
module-init-tools-3.3-0.pre3.1.60.el5
$

so our exercise here will be downloading the corresponding source package and perusing the source code for lsmod to see how it works.

And let me repeat -- other than the above package installs, most of what you see here can be done safely as a regular (non-root) user unless I specifically describe otherwise.

Setting up the yum repo files for source

CentOS does not come pre-configured with yum repository files for source packages, but it's easy enough to create them in your /etc/yum.repos.d/ directory. Create, say, the repo file source.repo and fill it with:

[base-SRPMS]
name=CentOS-$releasever - Base SRPMS
baseurl=http://mirror.centos.org/centos/$releasever/os/SRPMS/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5
priority=1
enabled=1

#released updates
[update-SRPMS]
name=CentOS-$releasever - Updates SRPMS
baseurl=http://mirror.centos.org/centos/$releasever/updates/SRPMS/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5
priority=1
enabled=1

And now, let's test it.

Downloading a source RPM file

As long as things have gone well so far, you should be able to (again, as a non-root user and somewhere under your home directory), download the required source RPM file with:

$ yumdownloader --source module-init-tools
Loaded plugins: fastestmirror
module-init-tools-3.3-0.pre3.1.60.el5.src.rpm
$

This should have dumped the source RPM file into your current directory. The question is now -- what can you do with it? And you have a couple options.

rpm2cpio

If you simply want to unload the source file exactly as is, you can use the rpm2cpio command to first examine its contents:

$ rpm2cpio module-init-tools-3.3-0.pre3.1.60.el5.src.rpm | cpio -it
blacklist-compat
depmod.conf.dist
modprobe.conf.dist
module-init-tools-3.3-pre3.tar.bz2
module-init-tools-3.3-pre3.tar.bz2.sign
module-init-tools-abort-on-dependents-fail.patch
module-init-tools-add-sysv-locking.patch
module-init-tools-allconf.patch
module-init-tools-check-override.patch
module-init-tools-depmod-doc-dash-a-fix.patch
module-init-tools-dump_modversions.patch
module-init-tools-fix-modprobe-ro-work-in-non-blocking-mode.patch
module-init-tools-fix-wildcard-matching.patch
module-init-tools-insmod-options.patch
module-init-tools-modinfo-multiple-kernels.patch
module-init-tools-modsig.patch
module-init-tools-noescape.patch
module-init-tools-quiet_alias_errors.patch
module-init-tools-read-kcmdline.patch
module-init-tools-show-depends.patch
module-init-tools-sort-modules.patch
module-init-tools.spec
weak-modules
618 blocks
$

The above will work just fine, but you'll notice that there are a number of patch files that will have been applied to the pristine source before building the package and you probably want to see the source after all that patching was done so, while rpm2cpio might work fine, there is a slightly more involved but more precise approach you can take.

Creating an RPM-building environment

For the purpose of "installing," prepping and building source RPM files, you should first create a local directory structure to hold the results of your work. First, create the structure under, say, your own rpmbuild directory:

$ mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

then configure the rpm command to know about it:

$ echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros

At this point, we're ready to start ripping into our module-init-tools source file.

"Installing" your source file

Unlike regular RPM files (which are normally installed into various system directories), source RPM files will be "installed" into wherever you configured rpm to install source RPM files (tautologically speaking).

If you just created your personal working RPM directory, you can see that it's currently fairly empty:

$ find rpmbuild
rpmbuild
rpmbuild/BUILD
rpmbuild/SPECS
rpmbuild/SRPMS
rpmbuild/RPMS
rpmbuild/SOURCES
$

At this point, you can "install" the downloaded module-init-tools source package file with:

$ rpm -ivh module-init-tools-3.3-0.pre3.1.60.el5.src.rpm 
   1:module-init-tools      warning: user mockbuild does not exist - using root
warning: group mockbuild does not exist - using root
warning: user mockbuild does not exist - using root
warning: group mockbuild does not exist - using root
... yap yap yap ...
########################################### [100%]
$

at which point, you can see the result:

$ find rpmbuild
rpmbuild
rpmbuild/BUILD
rpmbuild/SPECS
rpmbuild/SPECS/module-init-tools.spec
rpmbuild/SRPMS
rpmbuild/RPMS
rpmbuild/SOURCES
rpmbuild/SOURCES/depmod.conf.dist
rpmbuild/SOURCES/module-init-tools-show-depends.patch
rpmbuild/SOURCES/module-init-tools-allconf.patch
rpmbuild/SOURCES/module-init-tools-sort-modules.patch
rpmbuild/SOURCES/module-init-tools-fix-wildcard-matching.patch
rpmbuild/SOURCES/module-init-tools-read-kcmdline.patch
rpmbuild/SOURCES/weak-modules
rpmbuild/SOURCES/module-init-tools-3.3-pre3.tar.bz2
rpmbuild/SOURCES/module-init-tools-3.3-pre3.tar.bz2.sign
rpmbuild/SOURCES/modprobe.conf.dist
rpmbuild/SOURCES/module-init-tools-check-override.patch
rpmbuild/SOURCES/module-init-tools-fix-modprobe-ro-work-in-non-blocking-mode.patch
rpmbuild/SOURCES/module-init-tools-dump_modversions.patch
rpmbuild/SOURCES/module-init-tools-insmod-options.patch
rpmbuild/SOURCES/blacklist-compat
rpmbuild/SOURCES/module-init-tools-noescape.patch
rpmbuild/SOURCES/module-init-tools-depmod-doc-dash-a-fix.patch
rpmbuild/SOURCES/module-init-tools-modsig.patch
rpmbuild/SOURCES/module-init-tools-add-sysv-locking.patch
rpmbuild/SOURCES/module-init-tools-abort-on-dependents-fail.patch
rpmbuild/SOURCES/module-init-tools-quiet_alias_errors.patch
rpmbuild/SOURCES/module-init-tools-modinfo-multiple-kernels.patch
$

Excellent. Now we're ready to dig into that source. But first ...

rpmbuild

If it's not installed already, you'll need to install the rpm-build package for the rpmbuild command (note the need for root privilege here):

# yum install rpm-build

and we're finally ready to poke around the source for the lsmod command.

rpmbuild and package spec files

If you wander into your RPM-building directory structure to the SPECS directory, you'll notice that installing that source package resulted in the appearance of the "spec" file for the package:

$ cd rpmbuild/SPECS
$ ls
module-init-tools.spec
$

You can, on your own time, examine the spec file to see how a package is defined, but all we want to do is process the source to the point where we've extracted the pristine source for the package and applied all the patches. And that's easy -- run the following from within that SPECS directory:

$ rpmbuild -bp module-init-tools.spec 
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.78132
... more yap yap yap, including patching ...

The option -bp is short for "build and prep", which means that we don't want to do a full build (compile) of the package, we simply want to un-tar the source and apply the patches to it, so what we end up with is the source used to build the final binary package. So we can finally go look at that prepped source.

Finally, the source

The prepped package source will have been installed here:

$ cd ~/rpmbuild/BUILD/module-init-tools-3.3-pre3
$ ls
... much output here, including lsmod.c ...
$

so we can finally examine the source code for the lsmod command, which we find is (surprisingly?) simple:

$ cat lsmod.c
... snip ...
int main(int argc, char *argv[])
{
        char line[4096];
        FILE *file;

        try_old_version("lsmod", argv);

        if (argc != 1)
                print_usage("lsmod");

        file = fopen("/proc/modules", "r");
        if (!file) {
                perror("Opening /proc/modules");
                exit(1);
        }
... more snip ...

You're welcome to examine that source code on your own time, but the above snippet should be above to convince you that lsmod does little more than reproduce the contents of the /proc/modules file, except with nicer formatting.

And that, as they say, is that. Questions?

UPDATE: Installing source packages from EPEL

If you want to configure yum to pick up packages from the Extra Packages for Enterprise Linux (EPEL) repository, you can install the appropriate repository file thusly (use "x86_64" instead of "i386" depending on your architecture):

# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarc...

and if you do, note that that repository file already comes set up to download the source packages:

... snip ...
[epel-source]
name=Extra Packages for Enterprise Linux 5 - $basearch - Source
#baseurl=http://download.fedoraproject.org/pub/epel/5/SRPMS
mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-5&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL
gpgcheck=1

so there's no extra work to be done for EPEL packages.