RPM C programs are C programs that call on functions in the RPM library, often called rpmlib. To use the rpmlib, you need to set up a C programming environment and install the rpm-devel package.
15.1.1. Setting Up a C Programming Environment
At the very least, you’ll need a C compiler, gcc, and a text editor. The easiest way to get the C compiler is to install the packages grouped under Software Development with the Red Hat package management tool.
Cross Reference
The gcc package requires a number of capabilities. Make sure you install all the necessary packages. Just about every Linux distribution includes gcc and everything you need to develop C programs, so this should not be a problem.
For text editors, you can use the vi or emacs text editors, or any of a number of graphical editors such as gedit.
Cross Reference
Once you have a C programming environment set up, you next need to get the RPM library for an RPM development environment.
15.1.2. Setting Up the RPM Programming Environment
To program with the RPM library, you need to install the rpm-devel package. You must have a version of rpm-devel that matches your version of the rpm package. If you have Red Hat Linux, your installation CDs will also have the version of the RPM development package that corresponds to your system.
Your program should link against the same libraries that are used by the rpm command itself in order to insure compatibility, so make sure that the version of the rpm-devel package matches the rpm package itself. In most cases, the best bet is to use the RPM programs and libraries that come with your version of Linux.
Cross Reference
You can also download the rpm packages from ftp://ftp.rpm.org/pub/rpm/dist/. This site includes versions of the RPM libraries going back to 1996, ancient history in terms of Linux.
The package you need is rpm-devel. If you installed Red Hat Linux 8.0, the package is rpm-devel-4.1-1.06. This package includes header files, documentation, and libraries.
15.1.3. Using the RPM Library
All C programs using the RPM library need to include the file rpmlib.h, which defines the core data structures, constants, and functions. One thing you’ll quickly note is that the RPM C library accesses RPM data at a very low level. This is one reason why many developers are moving to Python for their RPM programs, since the Python RPM API presents a higher level of abstraction.
Cross Reference
In addition to rpmlib.h, the header file rpmcli.h defines a high-level API based on the command-line options to the rpm command. (The cli in rpmcli stands for command-line interface.) Table 16-1 lists other important RPM header files that make up the major subsystems of the RPM system.
Table 16-1 RPM sub-system header files
In addition, a number of header files define the major data objects in the RPM system and the functions that operate on these data objects. Table 16-2 lists these header files.
Table 16-2 RPM data object header files
All the RPM include files are located in /usr/include/rpm on most versions of Linux.
Note
You can use the rpm command and the queries introduced in
Chapter 4, Using the RPM Database to determine exactly where the header files are located. Simply execute the following command:
$ rpm –ql rpm-devel
Examine the output of this command for include files.
15.1.4. Compiling and Linking RPM Programs
RPM programs using the rpmlib C API are the same as C programs everywhere. You need to include the proper header files that define the API calls you need, and link with the right set of libraries.
The rpm include files are located in /usr/include/rpm, so you should add this directory to the set of directories that the C compiler looks in for include files with the –I command-line option. For example:
$ gcc –I/usr/include/rpm –c rpm1.c
Note
This also means that you can install the rpm header files in other directories as needed, and just change the –I command-line option.
To help debug problems, you probably want to add the -Wall (output all warnings) and -g (compile with debugging information). For example:
$ gcc -Wall -g –I/usr/include/rpm –c rpm1.c
The main rpm library is librpm.a, or a shared version of this same library. To do most anything with RPM programming, you need to link in the following libraries, as listed in Table 16-3.
Table 16-3 Required rpm libraries
If you are creating RPMs from your C programs, you also need to link in the rpmbuild library. To compile and link a simple RPM program, you need a command like the following:
gcc -I/usr/include/rpm -o program program.c –lrpmbuild \
-lrpm -lrpmdb -lrpmio –lpopt
On some versions of Linux or on other operating systems, you’ll likely need to link a set of helper libraries, as shown following:
gcc -I/usr/include/rpm -o program program.c –lrpmbuild \
-lrpm -lrpmdb -lrpmio –lpopt -lelf -lbz2 -lz
If you have installed the rpm libraries in a non-standard directory, you need to use the –L option to specify where else to look for libraries. For example:
gcc -I/usr/include/rpm -o program program.c –L/opt/lib/rpm \
-lrpmbuild -lrpm -lrpmdb -lrpmio –lpopt -lelf -lbz2 -lz
The -L option tells the cc compiler to look in the /opt/lib/rpm directory as well as in the standard locations such as /usr/lib.
Note
Starting with RPM 4.2, you should just need to link in the rpm library. The other libraries will get pulled in automatically if needed.
15.1.5. Getting information on your RPM environment
A large part of the RPM system lies in system-specific configuration, including the platform you are running on, compatible platforms, and locations of various files. The RPM rc and macro systems support hundreds of options tuned to the specifics of your system, and any customizations you have configured.
Cross Reference
Your C programs need to access these RPM system settings to ensure that all data values are properly set up for your system architecture and installation. So, to start an RPM C program, you need to read in all the configuration files. To do this, call rpmReadConfigFiles.
int rpmReadConfigFiles(const char *files, const char *target);
The files parameter holds a colon-delimited list of files that make up your system’s configuration. The target parameter holds the target platform. You can pass NULL for both these parameters to use the RPM defaults, which is generally what you want.
The rpmReadConfigFiles function returns a 0 on success, or –1 on errors.
Once you have read in the configuration files, you can access values in the configuration, or print it out.
15.1.5.1. Printing the Configuration
To print out the configuration, call rpmShowRC.
int rpmShowRC(FILE* output);
Pass in an output file to print the configuration to, such as stdout. For example:
rpmShowRC( stdout );
The rpmShowRC function always returns 0.
To control some of the output from rpmShowRC, and other RPM library functions, you can set the logging verbosity level by calling rpmSetVerbosity:
void rpmSetVerbosity(int level);
For example:
rpmSetVerbosity(RPMMESS_NORMAL);
Table 16-4 lists the verbosity levels from rpmio/rpmmessages.h going from least output to more output.
Table 16-4 Output verbosity levels
You can put together a simple RPM program such as the one shown in Listing 16-1.
Listing 16-1: rpm1.c
/* Show the rpmrc settings. */
#include <stdio.h>
#include <stdlib.h>
#include <rpmlib.h>
int main(int argc, char * argv[]) {
int status = rpmReadConfigFiles( (const char*) NULL,
(const char*) NULL);
if (status != 0) {
printf("Error reading RC files.\n");
exit(-1);
} else {
printf("Read RC OK\n");
}
rpmSetVerbosity(RPMMESS_NORMAL);
rpmShowRC( stdout );
exit(0);
}
Compile this program with a command like the following:
$ cc -I/usr/include/rpm -o rpm1 rpm1.c -lrpm -lrpmdb -lrpmio –lpopt
When you run this program, you should see the contents of your configuration printed to the screen.
15.1.5.2. Expanding the Value of Macros
With all the rc and macro configuration files, the RPM system has a lot of values, usually called macros, that you can use to refer to settings. The term macro is used because the values can be more than simple strings. You can have one macro refer to the value of other macros, for example. The basic macro syntax is:
%name_of_macro
For example:
%_target
Note
Most of the internal RPM macros start with an underscore, _.
You can expand a macro with the rpm --eval command:
$ rpm --eval %_target
i386-linux
You can also refer to a macro using the following syntax:
%{name_of_macro}
For example:
%{_target}
This syntax makes it easier to include a macro in combinations with other text and other macros, since it clearly delineates the macro name.
Cross Reference
Chapter 20, Customizing RPM Behavior covers macros in depth. In your C programs, your code will likely need to expand the value of macros to place data in the proper directories, determine the platform architecture, and so on.
15.1.5.3. Expanding Macros in Your Code
You can use rpmExpand to determine the value of system macros from within your C programs.
The rpmExpand function can expand the values of one or more macros, returning the expanded value. You can pass a variable number of parameters to rpmExpand, and you must terminate the list with a NULL:
char* rpmExpand (const char *arg,...);
You need to free the data returned by rpmExpand by calling free.
The program in Listing 16-2 takes the first command-line argument to your program (after the program name) and expands that argument as a macro.
Listing 16-2: rpmexpand.c
/* Show some macro settings. */
#include <stdio.h>
#include <stdlib.h>
#include <rpmlib.h>
#include <rpmmacro.h>
int main(int argc, char * argv[]) {
int status = rpmReadConfigFiles( (const char*) NULL,
(const char*) NULL);
if (status != 0) {
printf("Error reading RC files.\n");
exit(-1);
}
char* value = rpmExpand(argv[1], (const char*) NULL);
printf("Value of macro is [%s]\n", value);
exit(0);
}
Compile and link this program as shown previously.
When you run this program, pass the name of a macro to expand. For example:
$ ./rpmexpand %_target
Value of macro is [i386-linux]
You can pass multiple macros together, as shown following:
$ ./rpmexpand %_builddir/%_target
Value of macro is [/usr/src/redhat/BUILD/i386-linux]
You can verify this program with the rpm --eval command, introduced previously:
$ rpm --eval %_builddir/%_target
/usr/src/redhat/BUILD/i386-linux