Building XML-RPC Clients in C
XML-RPC is a useful protocol for building cross-platform, client-server applications. Often XML-RPC is demonstrated with high-level interpreted languages like Perl and Python. In this article, Eric Kidd's XML-RPC C library is used to build a simple, yet powerful debugging client. Special care is taken to bring programmers with rusty C-hacking skills up to speed.
When It Absolutely Has to Be in C
XML-RPC is a wire protocol that describes an XML serialization format that clients and servers use to pass remote procedure calls to each other. There are two features that make this protocol worth knowing. The first is that the details of parsing the XML are hidden from the user. The second is that clients and servers don't need to be written in the same language. For more background information on XML-RPC, check out the Resources listed at the end of this article.
Many articles written about XML-RPC use Java, Perl, Python, or PHP to demonstrate building Web Services. However, real life programming requirements often mitigate against the programmer's first choice of implementation language, perhaps because access to a resource whose only API is a C library is required. In this case, building an XML-RPC server to this resource opens it up to any client that supports XML-RPC.
In high-level languages like Perl or Python, development can be rapid because the compile-run-edit test cycle is almost as fast as opening one's editor. In C, this isn't the case. Compiling a program can take a lot of time. But sometimes it's the only tool for the job. If your C skills are rusty, this article is for you. Because XML-RPC is a high-level protocol, it takes a good number of other C libraries to make the magic happen. Remember, even a simple client needs to be able to talk HTTP and parse XML, both of which are far beyond the built-in facilities of C. The first step is installing Eric Kidd's C library.
Installing the C XML-RPC Library
Eric Kidd's C/C++ interface to XML-RPC requires the W3 Consortium's
WWW library. I used version
0.9.9 of the XML-RPC library and version 5.3.2 of the W3C's libwww for
this article. Both packages compiled cleanly on my Red Hat 7.1 box
using the standard ./configure && make, but there was a
catch during the make install phase. The names of the
shared libraries had an spurious '.0' appended to them. Those already
familiar with the way shared libraries are found on a Linux box may
skip the next section.
Joe Johnston has also written an article on Binary Data to Go: Using XML-RPC to Serve Up Charts on the Fly, which looks at how XML-RPC and Web services can create simple charts for client programs.
Both Kidd's and the W3C's libraries install shared libraries. These
differ from the static libraries you might be more familiar with in that they
are loaded during a program's runtime rather than being built into
a monolithic static binary. In order for the system to find these shared
libraries, you need to make sure that /etc/ld.so.conf lists
the path to your shared libraries. In the case of the XML-RPC libraries
listed above, /usr/lib must be listed. For existing binaries,
you can check whether the system can locate the shared libraries by using the
ldd command:
$ ldd <executable_name>
In the case of the program presented here, the ldd output will look something like:
[jjohn@marian src]$ ldd xmlrpc_debug
libwwwxml.so.0 => /usr/lib/libwwwxml.so.0 (0x4002f000)
libxmltok.so.0 => /usr/lib/libxmltok.so.0 (0x40032000)
libxmlparse.so.0 => /usr/lib/libxmlparse.so.0 (0x40044000)
libwwwzip.so.0 => /usr/lib/libwwwzip.so.0 (0x4004b000)
libwwwinit.so.0 => /usr/lib/libwwwinit.so.0 (0x4004e000)
libwwwapp.so.0 => /usr/lib/libwwwapp.so.0 (0x40051000)
libmd5.so.0 => /usr/lib/libmd5.so.0 (0x40067000)
libwwwhtml.so.0 => /usr/lib/libwwwhtml.so.0 (0x4006a000)
libwwwtelnet.so.0 => /usr/lib/libwwwtelnet.so.0 (0x40076000)
...
If a library doesn't have a hex address next to it, the system
can't find it. This can be solved by making sure that path in
/etc/ld.so.conf is listed and then running
ldconfig as root. Run the ldd command again
to make sure all your shared libraries are visible.
As I mentioned above, the gotcha is that Kidd and W3C libraries
don't name the shared libraries correctly (they install with an extra
'.0' on the name). You will need to create symlinks with the kind of
names that ldconfig expects. I used the Perl script in Listing 1 to do this, but any scripting tool
will do.
Listing 1: Fixing library names
1 #!/usr/bin/perl --
2 # Look for these libs; ensure symlinks are right
3
4 use strict;
5 use constant LIB_DIR => '/usr/lib';
6
7 my @libs = qw(
8 libwwwxml libxmltok libxmlparse libwwwzip
9 libwwwinit libwwwapp libmd5 libwwwhtml libwwwtelnet
10 libwwwnews libwwwhttp libwwwmime libwwwgopher libwwwftp
11 libwwwfile libwwwdir libwwwcache libwwwstream libwwwmux
12 libwwwtrans libwwwcore libwwwutils libdl libz
13 libxmlrpc libxmlrpc_xmlparse libxmlrpc_xmltok
14 );
15 if( $> != 0 ){
16 warn "ERROR: Must be root\n";
17 }
18
19 chdir LIB_DIR || die "ERROR: cd: $!";
20
21 for my $lib (@libs){
22 print "Looking for $lib\n";
23 my @candidates = glob("$lib*0");
24 for(@candidates){
25 my ($link, $real);
26 if( -l $_ ){
27 $real = readlink($_);
28 $link = "(is a symlink to $real)";
29 }
30 print "\t$_ $link\n";
31
32 if( $real ){
33 my $new = substr($_, 0, length($_) - 2);
34 print "\tcreating a new symlink($new) to $real\n";
35 unless( symlink $real, $new ){
36 warn "WARN: symlink: $!\n";
37 }
38 }
39 }
40 }
|
Once you've squared this detail away, you're ready to start coding.