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

15.4. Programming with the RPM Database

Many functions in rpmlib require a transaction set. In particular, accessing the rpm database is quite easy using a transaction set.

Create a transaction set by calling rpmtsCreate:

rpmts rpmtsCreate(void);

RPM uses transaction sets to bracket operations on the RPM database. As the RPM API evolves, transaction sets will become more and more important. Transaction sets also help in that the RPM library will automatically open the RPM database as needed.

When you are done with a transaction set, call rpmtsFree:

rpmts rpmtsFree(rpmts ts);

The call to rpmtsFree returns NULL.

15.4.1. Database iterators

Once you have a transaction set, you can iterate over the installed packages in the RPM database by creating an iterator. To do this, call rpmtsInitIterator:

rpmdbMatchIterator rpmtsInitIterator(const rpmts ts,

rpmTag rpmtag,

const void *keypointer,

size_t keylen);

You need to specify which tag to iterate by, which in most cases will be the package name, RPMTAG_NAME, introduced previously With the RPMTAG_NAME tag, you need to pass the name of a package to look for in the keypointer parameter. (The keypointer varies based on the tag you pass.)

For string data, you can pass 0 for the keylen parameter. For example, this call to rpmtsInitIterator looks for all packages named sendmail.

rpmdbMatchIterator iter;

iter = rpmtsInitIterator(ts, RPMTAG_NAME, "sendmail", 0);

The rpmdbMatchIterator allows you to iterate through a number of packages, in this case, all the packages that match a given name. After calling rpmtsInitIterator, the next step is to call rpmdbNextIterator:

Header rpmdbNextIterator(rpmdbMatchIterator iter);

This function returns the next package Header object in the iterator. The Header will be NULL if there are no more packages in the iterator.

If the Header is not NULL, you can get entries from it, as shown previously. You can use a while loop to go through all the matching packages. For example:

while ( (installed_header = rpmdbNextIterator(iter) ) != NULL) {

/* Do something... */

}

Note

In future versions of the RPM library, rpmtsNextIterator, will replace rpmdbNextIterator.

You do not need to free the Header returned by rpmdbNextIterator. Also, the next call to rpmdbNextIterator will reset the Header.

You can customize how an iterator works by adding a pattern to the iterator with rpmdbSetIteratorRE:

int rpmdbSetIteratorRE(rpmdbMatchIterator iter,

rpmTag tag,

rpmMireMode mode,

const char * pattern);

Calling rpmdbSetIteratorRE modifies the passed-in iterator to use the given pattern as a further test on the given tag. The mode parameter names the type of pattern used, which can be one of those listed in Table 16-9.

Table 16-9 Types of patterns for rpmdbSetIteratorRE

Type

Meaning

RPMMIRE_DEFAULT

Same as regular expressions but with \., .*, and ^..$ added.

RPMMIRE_GLOB

Glob-style patterns using fnmatch.

RPMMIRE_REGEX

Regular expressions using regcomp.

RPMMIRE_STRCMP

String comparisons using strcmp.

Cross Reference

For more on these patterns, see the online manual pages for fnmatch(3), glob(7), regcomp(3), regex(7), and strcmp(3).

Free the iterator when done with rpmdbFreeIterator:

rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator iter);

The call to rpmdbFreeIterator returns NULL.

15.4.2. Dependency Sets

To compare package versions, create a dependency set. The rpm command, for example, uses dependency sets to compare package versions.

Note

You could compare the version numbers directly, calling headerGetEntry to get the version and release tags, converting these strings to numbers and then comparing, but this would cause problems. The custom comparison is not as exact as the code in this section, especially since many packages have version numbers that are not true numbers, such as 1.12.4, with one too many decimal points. This makes the comparisons harder. In addition, there is more than just the version number to take into account. You need to deal with the Epoch value, as well as the release, too.

To handle all the complicated logic of comparing versions, you can use the code in this section, or call rpmvercmp. Do not try to compare version numbers with custom code.

To create a dependency set for a given package Header, call rpmdsThis. Calling rpmdsThis creates a dependency set that holds a triple of the package name, the Epoch/Version/Release information, and the flags.

rpmds rpmdsThis(Header header,

rpmTag tagID,

int_32 Flags);

For comparing packages, you can pass RPMTAG_REQUIRENAME for the tagID. The actual tagID here is ignored for the version check. What you do need, though, are flags to check whether another package is less than or equal to the Epoch/Version/Release information in this dependency set. For this task, pass the following bit flags:

(RPMSENSE_EQUAL|RPMSENSE_LESS)

Once you have a dependency set, you can use the handy function rpmdsNVRMatchesDep to compare the NVR, or Name, Version, Release entries in the header of one package against the data in the dependency set.

int rpmdsNVRMatchesDep(const Header header,

const rpmds dependency_set,

int nopromote);

After checking the dependencies, rpmdsNVRMatchesDep returns 1 if the dependency overlaps, or 0 otherwise. In terms of comparing packages, 1 means that the package file is as old or older than the installed package, and 0 means that the package already installed is newer. Pass 1 to prevent promoting the Epoch value in the packages during the comparison.

The actual comparison is controlled by the call that creates the dependency set, especially the flags. Thus, passing flags of (RPMSENSE_EQUAL|RPMSENSE_LESS) to rpmdsThis set up the test as a less than or equal test.

Note

The RPM C API documentation marks rpmdsNVRMatchesDep as deprecated, to be replaced in the future.

You can also call rpmVersionCompare to compare the versions of two packages:

int rpmVersionCompare(Header header1, Header header2);

The return value is -1 if the header1 represents an older version than header2, 0 if the two headers represent the same version, and 1 if header1 represents a newer version than header2.

To get the name of the package from a dependency set, call rpmdsN:

const char* rpmdsN(const rpmds dependency_set);

You can use rpmdsN to get the name when calling rpmtsInitIterator if you are working with dependency sets when searching the RPM database.

Free a dependency set when done by calling rpmdsFree:

rpmds rpmdsFree(rpmds dependency_set);

As with other free functions, rpmdsFree returns NULL.

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