initCommon(); $template->displayHeader(); ?>

17.3. Programming with the RPM Database

In addition to providing query routines for RPM files, you can also access the RPM database with the RPM2 package.

To access the RPM database, your Perl script must first open the database.

17.3.1. Opening the database

Open the RPM database with a call to open_rpm_db on the RPM2 object. For example:

my $rpm_db = RPM2->open_rpm_db();

You can also specify the directory where the RPM database resides. This is most useful for accessing a database in a non-standard location. For example:

my $rpm_db = RPM2->open_rpm_db( "-path" => "/var/lib/rpm" );

Note

The -path is normally used as a Perl bareword but is shown here as a string.

Once you have an RPM database object, you can call one of the find subroutines to find packages in most of the same ways as supported by the rpm –q command.

17.3.2. Finding packages

c

The find_by_name subroutine finds a package or packages by name. It returns a Perl list of the entries found. For example, if you installed more than one version of a package, find_by_name would return a list of all the packages at the different versions.

Similar to find_by_name, find_by_name_iter returns an iterator to iterate over the packages that match the query. The iterator approach is usually more efficient.

17.3.3. Iterating over packages

Iterators are important in the RPM2 package because they provide a more efficient interface to potentially large sets of packages, and because iterators more closely match the underlying C API. Furthermore, iterators are very easy to use. Simply call the next subroutine to move ahead to the next element, that is, the next package.

For example:

my $pkg_iter = $rpm_db->find_by_name_iter( "kernel" );

while (my $pkg = $pkg_iter->next() ) {

# Do something ...

}

Listing 18-3 shows a script that acts much like the rpm –q command, without any other command-line options.

Listing 18-3: rpmname.pl

#!/usr/bin/perl

#

# Queries RPM database for given package.

# Usage:

# rpmname.pl package_name

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db( "−path" => "/var/lib/rpm" );

my $pkg_iter = $rpm_db->find_by_name_iter( $ARGV[0] );

while (my $pkg = $pkg_iter->next() ) {

print $pkg->tag("NAME"), "-", $pkg->tag("VERSION"), "\n";

}

$rpm_db->close_rpm_db();

When you run this script, you need to pass the name of a package to query. For example:

$ ./rpmname.pl kernel

kernel-2.4.18

17.3.4. Additional query subroutines

The find_by_name_iter subroutine finds a package by its name. The RPM2 module also supports a number of other query routines, listed in Table 18-1.

Table 18-1 RPM2 module query routines

Routine

Usage

find_all()

Returns a list with all the packages in the database

find_all_iter()

Returns an iterator over all the packages in the database

find_by_file($filename)

Finds all packages that own the given file, returning a list

find_by_file_iter($filename)

Finds all packages that own the given file, returning an iterator

find_by_name($package_name)

Finds all packages with the given name, returning a list

find_by_name_iter($package_name)

Finds all packages with the given name, returning an iterator

find_by_provides($capability)

Finds all packages that provide the given capability, returning a list

find_by_provides_iter($capability)

Finds all packages that provide the given capability, returning an iterator

find_by_requires($capability)

Finds all packages that require the given capability, returning a list

find_by_requires_iter($capability)

Finds all packages that require the given capability, returning an iterator

To verify the find routines, you can try the following script and compare the results with the rpm command. Listing 18-4 shows the script that finds what package provides a capability and also which packages require the capability.

Listing 18-4: rpmprovides.pl

#!/usr/bin/perl

#

# Queries RPM database for given package,

# listing what it provides and what other

# packages require the capability.

#

# Usage:

# rpmprovides.pl package_name

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db();

my $pkg_iter = $rpm_db->find_by_provides_iter( $ARGV[0] );

print "Provides: ", $ARGV[0], "\n";

while (my $pkg = $pkg_iter->next() ) {

print "\t", $pkg->as_nvre(), "\n";

}

# Now, what packages require this capability.

my $pkg_iter2 = $rpm_db->find_by_requires_iter( $ARGV[0] );

print "Requires: ", $ARGV[0], "\n";

while (my $pkg2 = $pkg_iter2->next() ) {

print "\t", $pkg2->as_nvre(), "\n";

}

$rpm_db->close_rpm_db();

When you run this script with the name of a capability, you'll see output like the following:

$ ./rpmprovides.pl httpd

Provides: httpd

httpd-2.0.40-8

Requires: httpd

mod_perl-1.99_05-3

5:redhat-config-httpd-1.0.1-13

mod_python-3.0.0-10

1:mod_ssl-2.0.40-8

Note

The 5: in 5:redhat-config-httpd-1.0.1-13 and 1: in 1:mod_ssl-2.0.40-8 represent the EPOCH tag value.

To verify this script, run the rpm -q command to see if you get the same packages listed. For example:

$ rpm -q --whatprovides httpd

httpd-2.0.40-8

$ rpm -q --whatrequires httpd

mod_perl-1.99_05-3

redhat-config-httpd-1.0.1-13

mod_python-3.0.0-10

mod_ssl-2.0.40-8

In both cases, you see the same packages listed. You can use this technique to verify your scripts.

Note

The find_by_provides_iter subroutine requires the name of a package, such as bash. You cannot pass a file name, such as /bin/bash, to get the name of the package that provides this capability (a file, really).

17.3.5. Getting information on packages

ng information on packages

The tag, as_nvre, and is_source_package subroutines that worked on header objects read from RPM files, shown previously, also work with package entries returned from the RPM database.

For example, Listing 18-5 shows a script, rpminfo.pl, that prints out descriptive information about a given package.

Listing 18-5: rpminfo.pl

#!/usr/bin/perl

#

# Queries RPM database for given package and prints info.

# Usage:

# rpminfo.pl package_name

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db( "-path" => "/var/lib/rpm" );

my $pkg_iter = $rpm_db->find_by_name_iter( $ARGV[0] );

while (my $pkg = $pkg_iter->next() ) {

printInfo( $pkg );

}

$rpm_db->close_rpm_db();

# Prints info on one package.

sub printInfo {

my($pkg) = shift;

print $pkg->as_nvre(), ", ", $pkg->tag("ARCH"), ", ",

$pkg->tag("OS"), ", ", $pkg->tag("PLATFORM"), "\n";

print $pkg->tag("SUMMARY"), "\n";

print "Group: ", $pkg->tag("GROUP"), "\n";

print $pkg->tag("DESCRIPTION"), "\n";

print "Vendor: ", $pkg->tag("VENDOR"), ", ", $pkg->tag("URL"), "\n";

print "Size: ", $pkg->tag("SIZE"), "\n";

}

When you run this script, you’ll see output like the following:

$ ./rpminfo.pl XFree86

XFree86-4.2.0-72, i386, linux, i386-redhat-linux-gnu

The basic fonts, programs and docs for an X workstation.

Group: User Interface/X

XFree86 is an open source implementation of the X Window System. It

provides the basic low level functionality which full fledged

graphical user interfaces (GUIs) such as GNOME and KDE are designed

upon.

Vendor: Red Hat, Inc., http://www.xfree86.org

Size: 30552239

17.3.5.1. Listing the Installed Date

The installed date is a number value representing the number of seconds since the start of the UNIX epoch, January 1, 1970, which predates the start of the Linux epoch by about 20 years. So, when you get the value of the INSTALLTIME tag, you’ll see a meaningless number.

To make sense of this number, pass the value to the Perl localtime function. Listing 18-6 shows an example of this.

Listing 18-6: rpmdate.pl

#!/usr/bin/perl

#

# Queries RPM database for given package,

# prints out name, vendor, and date installed.

# Usage:

# rpmdate.pl package_name

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db();

my $pkg_iter = $rpm_db->find_by_name_iter( $ARGV[0] );

while (my $pkg = $pkg_iter->next() ) {

printDate( $pkg );

}

$rpm_db->close_rpm_db();

# Prints installation data for one package.

sub printDate {

my($pkg) = shift;

my $date = localtime( $pkg->tag("INSTALLTIME") );

printf("%-20s %-17s %s\n", $pkg->as_nvre(), $pkg->tag("VENDOR"), $date);

}

Note

The printf function in this script can do something the rpm command cannot do. Even with the --queryformat option, you cannot group multiple items and then set the size; with Perl, you can. Simply assign the multiple values to a string, or use the handy as_nvre subroutine, which gathers up to four tags together into one string.

When you pass the name of a package to this script, you’ll see the date the package was installed. For example:

$ ./rpmdate.pl kernel

kernel-2.4.18-14 Red Hat, Inc. Sat Oct 5 12:29:58 2002

17.3.5.2. Handling String Array Tags

Not only is the date stored in a format that adds complication to your script. A number of tags are string arrays, not scalar strings. This means you may see output that is all mashed together.

To help deal with this, the following subroutine takes in an array of strings and returns a string that is built using a passed-in delimiter:

sub arrayToString {

my($sep) = shift;

my(@array) = @_;

my($str);

$str = $array[0];

for ( $i = 1; $i < $#array; $i++ )

{

$str = $str . $sep . $array[$i];

}

return $str;

}

Note

Show your Perl expertise and earn extra points by implementing the arrayToString subroutine as a single Perl statement that uses the join function.

The following list shows the tags that are an array of strings:

*BASENAMES

*CHANGELOGNAME

*CHANGELOGTEXT

*DIRNAMES

*FILEGROUPNAME

*FILELANGS

*FILELINKTOS

*FILEMD5S

*FILEUSERNAME

*OLDFILENAMES

*PROVIDENAME

*PROVIDEVERSION

*REQUIRENAME

*REQUIREVERSION

Cross Reference

Chapter 5 covers more on these tags.

17.3.5.3. Listing the Files In A Package

The files subroutine provides a list of all the files in a package. Listing 18-7 shows how to access this list.

Listing 18-7: rpmfiles.pl

#!/usr/bin/perl

#

# Queries RPM database for given package,

# prints out the files in the package.

# Usage:

# rpmfiles.pl package_name

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db();

my $pkg_iter = $rpm_db->find_by_name_iter( $ARGV[0] );

while (my $pkg = $pkg_iter->next() ) {

printFiles( $pkg );

}

$rpm_db->close_rpm_db();

# Prints installation data for one package.

sub printFiles {

my($pkg) = shift;

my $files = arrayToString("\n", $pkg->files() );

print "Files:\n", $files, "\n";

}

sub arrayToString {

my($sep) = shift;

my(@array) = @_;

my($str);

$str = $array[0];

for ( my $i = 1; $i < $#array; $i++ )

{

$str = $str . $sep . $array[$i];

}

return $str;

}

When you run this script, you’ll see output like the following:

$ ./rpmfiles.pl jikes

Files:

/usr/bin/jikes

/usr/doc/jikes-1.17/license.htm

17.3.6. Comparing versions

The RPM2 module overrides the spaceship operator, <=>, to perform version comparisons between packages. The script in Listing 18-8 shows how to compare all local RPM files against the newest installed version of the same package, if the package is installed.

Listing 18-8: rpmver.pl

#!/usr/bin/perl -w

#

# Compare versions of all *.rpm files against the

# latest packages installed (if installed)

#

# Usage:

# rpmver.pl

# This script looks for all *.rpm files.

#

use strict;

use RPM2;

my $rpm_db = RPM2->open_rpm_db();

for my $filename (<*.rpm>) {

my $h = RPM2->open_package( $filename );

# Ensure we compare against the newest

# package of the given name.

my ($installed) =

sort { $b <=> $a } $rpm_db->find_by_name($h->name);

if (not $installed) {

printf "Package %s not installed.\n", $h->as_nvre;

} else {

my ($result) = ($h <=> $installed);

if ($result < 0) {

printf "Installed package %s newer than file %s\n",

$installed->as_nvre,

$h->as_nvre;

} else {

printf "File %s newer than installed package %s\n",

$h->as_nvre,

$installed->as_nvre;

}

}

}

The sort { $a <=> $b } in front of the find_by_name call sorts all the packages of that name by the version number, so that the comparison is performed against the newest installed version of the package. The ($h <=> $installed) compares the header from the RPM file on disk against the newest installed version of the package.

When you run this script, you’ll see output like the following, depending on which RPM files you have in the local directory:

$ perl rpmver.pl

Package acroread-4.0-0 not installed.

Package canvas-7.0b2.0-1 not installed.

Installed package jikes-1.18-1 newer than file jikes-1.14-1

Installed package SDL-1.2.4-5 newer than file SDL-0.9.9-4

Package ted-2.8-1 not installed.

17.3.7. Closing the database

When you are done with the RPM database, call close_rpm_db, as shown following:

$rpm_db->close_rpm_db();

Note that this call is not necessary, as the RPM2 module will close the database when the object, in this case $rpm_db, goes out of scope.

displayFooter('$Date: 2005/11/02 19:30:06 $'); ?>