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

15.3. Working with RPM Files

The RPM C library provides functions to read RPM files as well as query the RPM database. Going beyond querying, you can perform all the tasks that the rpm and rpmbuild commands do, since both these commands are written in C using the RPM library. That said, some tasks are much easier than other tasks. If you are writing a complex package installation program, or a program that keeps various systems up to date with regards to package versions, you may want to look at the Python RPM API instead of the C RPM library.

Cross Reference

Chapter 17 covers the Python RPM API.

15.3.1. Opening RPM files

When working with an RPM file from within a program, the first thing you need to do is open the file. Use Fopen:

FD_t Fopen(const char * path,

const char * fmode);

Fopen works like the standard C function fopen(3).

Note

The reason the RPM library wraps the input/output C library functions is to ensure portability to other operating systems. This is a fairly common technique.

15.3.2. Reading the RPM lead and signature

Once you have opened an RPM file, you can start to read header information, which is the most interesting information to most RPM programs. (You may also want to read the files in the RPM payload, for example.) Before you can start reading the header, though, you must read forward in the RPM file past the lead and signature.

Cross Reference

Chapter 3 introduces the lead and signature.

Even if your programs don’t want to examine the lead or signature, you must read past to position the file offset properly for reading the header information. To read past the lead, call readLead:

int readLead(FD_t fd, struct rpmlead *lead);

The readLead function returns 0 on success or 1 on an error. It fills in an rpmlead struct:

struct rpmlead {

unsigned char magic[4];

unsigned char major;

unsigned char minor;

short type;

short archnum;

char name[66];

short osnum;

short signature_type;

char reserved[16];

};

To read past the signature, call rpmReadSignature:

rpmRC rpmReadSignature(FD_t fd,

Header * header,

sigType sig_type);

The return code is one of the values listed in Table 16-8.

Table 16-8 Return codes from rpmReadSignature

Code

RPMRC_OK

RPMRC_BADMAGIC

RPMRC_FAIL

RPMRC_BADSIZE

RPMRC_SHORTREAD

You can do more with the signature than merely reading past it, of course. Look in the online RPM documentation for more on verifying signatures.

After reading the signature, you can start to read the general header entries.

15.3.3. Reading header information

The header information includes the package name, version, pre- and post-installation scripts, and so on. To read in the RPM header, call headerRead. If successful, headerRead returns a Header object. You can then read data values from the Header.

Header headerRead(FD_t fd,

enum hMagic magicp);

Note

When working with the RPM database, you will also use Header objects.

The trickiest thing about calling headerRead is that you must pass a special magic number flag. This value must be HEADER_MAGIC_YES if the header has a set of magic numbers, and HEADER_MAGIC_NO if not. If you guess incorrectly, headerRead will return an error. To get around, this, you can compare the major number in the lead. For example:

Header header = headerRead(fd, (lead.major >= 3) ?

HEADER_MAGIC_YES : HEADER_MAGIC_NO);

Note

This snippet is one of the gems you'll find when you browse the RPM source code. Use the source.

To read values from the Header, call headerGetEntry. To call headerGetEntry, you pass in a Header and a tag ID. You get back the type of the tag, a pointer to the tag values, and a count of the number of values stored under this tag.

int headerGetEntry(Header header,

int_32 tag,

hTYP_t type,

void **pointer,

hCNT_t data_size);

The call to headerGetEntry returns a 1 on success, or a 0 on failure. On success, the pointer will point at the retrieved data, with the type parameter set to one of the following enum values:

enum rpmTagType_e {

RPM_NULL_TYPE = 0,

RPM_CHAR_TYPE = 1,

RPM_INT8_TYPE = 2,

RPM_INT16_TYPE = 3,

RPM_INT32_TYPE = 4,

RPM_STRING_TYPE = 6,

RPM_BIN_TYPE = 7,

RPM_STRING_ARRAY_TYPE = 8,

RPM_I18NSTRING_TYPE

}

Note

If the type is RPM_STRING_ARRAY_TYPE or RPM_BIN_TYPE, you must free the pointer. Call headerFreeData to free the data:

void* headerFreeData(const void *pointer,

rpmTagType type);

You need to pass in the data pointer and the type flag. You can safely call headerFreeData for all types. The function will do nothing if the type is not set up to require freeing.

When you call headerGetEntry, you must identify the tag you want from the header. This tag is an identifier for the --queryformat tags introduced in Chapter 5. The file rpmlib.h lists the various tags, such as RPMTAG_NAME, RPMTAG_VERSION, and RPMTAG_RELEASE.

The following function shows how to read a string entry from a Header:

/* Function to read a string header entry. */

char* readHeaderString(Header header, int_32 tag_id) {

int_32 type;

void* pointer;

int_32 data_size;

int header_status = headerGetEntry(header,

tag_id,

&type,

&pointer,

&data_size);

if (header_status) {

if (type == RPM_STRING_TYPE) {

return pointer;

}

}

return NULL;

}

Pass the Header object and the ID of the tag to read. For example:

char* name = readHeaderString(header, RPMTAG_NAME);

char* version = readHeaderString(header, RPMTAG_VERSION);

char* release = readHeaderString(header, RPMTAG_RELEASE);

To just get the name, version, and release number, you can call the utility function headerNVR, which has the following function signature:

int headerNVR(Header header,

const char **nameptr,

const char **versionptr,

const char **releaseptr);

When you are through with a header, free it by calling headerFree:

Header headerFree(Header header);

The call to headerFree returns NULL, so you can use the call to set the original pointer to to NULL to prevent accidental reuse. For example:

header = headerFree(header);

15.3.4. A shortcut to header information

You can read in a Header using the shortcut utility method rpmReadPackageFile:

int rpmReadPackageFile(rpmts ts,

FD_t fd,

const char *filename,

Header *header);

You need to pass a transaction set to rpmReadPackageFile and an open file. The filename is just used for reporting errors. On success, rpmReadPackageFile fills in a Header object from the package file. The return value is 0 for success.

To get the necessary transaction set, you need to create one with rpmtsCreate, covered in the "Programming with the RPM Database" section, following.

Note

In most cases, you should call rpmReadPackageFile in place of readLead, rpmReadSignature, and headerRead, since rpmReadPackageFile also verifies the package integrity.

15.3.5. Closing RPM files

When you’re done with an RPM file, close it with Fclose:

int Fclose(FD_t fd);

Fclose acts much like the standard C function fclose(3). The FD_t is an RPM data type that is very similar to a FILE pointer.

The RPM I/O subsystem, defined with rpmio.h, includes functions that mimic (and in most cases wrap) the ANSI C stdio functions. These include: Fopen, Fclose, Fread, Fwrite, Ferror, Fflush, Fileno, and Fseek.

These functions wrap the ANSI C stdio functions to add new features. The Fopen function, for example, supports HTTP or FTP URLs in the place of a file name, so long as you append ".ufdio" to the mode.

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