Generate and Check RPC C Code with rpcgen
Feed a small RPC language definition to rpcgen and it hands back a C header, XDR routines, client stubs and server stubs in one pass. This walkthrough uses rpcgen 1.4.2 from the rpcsvc-proto package. Allow about 15 minutes for the first run, plus more time to adapt the interface to your own application.
The route
Jump straight to the step you need, or tick off Done means at the end.
Prerequisites
You need a shell, rpcgen, a C compiler and a writable working directory. None of the commands below need root. Use a private directory for generated files so an experiment cannot overwrite an existing source tree.
Checkpoint
If the version check works and you have a fresh working directory, move on to step 1. Missing rpcgen? Install the rpcsvc-proto package through your distribution's normal package manager, then repeat the check.
$ command -v rpcgen
/usr/bin/rpcgen
$ rpcgen --version
rpcgen (rpcsvc-proto) 1.4.2
1. Create a small RPC definition
Make a file named ping.x declaring one program, one version and one procedure. The procedure takes no argument and returns no value, which keeps the generated files easy to inspect. The hexadecimal program number is an example, not a registration for a production service.
$ mkdir -p "$HOME/rpcgen-demo"
$ cd "$HOME/rpcgen-demo"
$ cat > ping.x <<'EOF'
program PING_PROG {
version PING_VERS {
void PING(void) = 1;
} = 1;
} = 0x20000001;
EOF
rpcgen runs the C preprocessor over the input before interpreting it. That makes preprocessor directives and conditional definitions useful, but it also means a stray macro can silently change the interface. Keep the definition reviewable, and reach for rpcgen -DNAME only when you actually have a conditional branch to select.
2. Generate the normal file set
Run rpcgen with the definition as its only input. With ping.x, normal mode derives four names from the base name: a header, XDR routines, client-side stubs and server-side stubs.
$ rpcgen ping.x
$ printf '%s\n' ping.h ping_xdr.c ping_clnt.c ping_svc.c | xargs -n1 test -f
$ ls -1
ping.h
ping.x
ping_clnt.c
ping_svc.c
ping_xdr.c
A successful command is not proof the interface is right. Open the generated header and check for the names you expect:
$ grep -E 'PING_(PROG|VERS)|ping_1' ping.h
#define PING_PROG 0x20000001
#define PING_VERS 1
extern void * ping_1(void *, CLIENT *);
extern void * ping_1_svc(void *, struct svc_req *);
Checkpoint
Stop here if any generated file has an unexpected name, or if an old file just got replaced. Restore it from version control or backup before continuing. Never run rpcgen in a directory holding hand-edited generated files unless you have deliberately chosen to overwrite them.
3. Generate one output deliberately
Use a mode flag when you need only one class of output. -h writes C data definitions, and -o names the output explicitly. Without -o, these single-output modes print to standard output, handy for inspection but easy to lose in a scrolling terminal.
$ rpcgen -h -o checked-ping.h ping.x
$ grep -E 'PING_(PROG|VERS)' checked-ping.h
#define PING_PROG 0x20000001
#define PING_VERS 1
The other focused modes: -c for XDR routines, -l for client stubs, -m for server stubs without a generated main routine, -t for a dispatch table, and -s or -n for server-side stubs tied to a transport class or identifier. These flags describe what gets generated; they do not compile anything.
4. Build a dispatch-table variant when needed
If your server needs an RPC dispatch table, add -T. It is a global option, so use it alongside normal generation and check for the extra table file:
$ mkdir -p dispatch
$ cd dispatch
$ rpcgen -T -o ping.h ../ping.x
$ rpcgen -T -c -o ping_xdr.c ../ping.x
$ rpcgen -T -l -o ping_clnt.c ../ping.x
$ rpcgen -T -m -o ping_svc.c ../ping.x
$ rpcgen -T -t -o ping_tbl.i ../ping.x
$ test -s ping_tbl.i && echo "dispatch table generated"
dispatch table generated
Spelling out each file this way avoids guessing which files a later build step will regenerate. For the ordinary five-file set, rpcgen -T ping.x is shorter and produces the table alongside the header and stubs in one go.
5. Check generated C before integrating it
Generated code includes <rpc/rpc.h>, so a clean rpcgen run says nothing about whether your compiler actually has the RPC headers and library. Check the generated text first, then ask the compiler for syntax-only validation using your system's own include and library layout:
$ grep -nE '^#include|_RPCGEN|ping_1' ping.h ping_clnt.c ping_svc.c ping_xdr.c
$ cc -fsyntax-only -I. ping_xdr.c ping_clnt.c ping_svc.c
$ echo $?
0
Compiler cannot find rpc/rpc.h? That is a missing development package or include path, not a flaw in your RPC definition. Reports a duplicate identifier or an unexpected procedure signature instead? Check the program, version, procedure and type names in ping.x. The manpage warns that apparent scoping does not prevent name clashes, so keep names unique across a larger interface.
6. Choose service behaviour consciously
Normal server generation creates a static service for the transports selected with -s. -I also makes a service suitable for starting from inetd. A generated service can create handles for transports named by NETPATH; when that variable is unset, it falls back to the visible transports in /etc/netconfig. These are run-time choices, not compile-time ones.
Do not deploy a generated server just because it compiles. Review the transport and launcher configuration, restrict exposure with your host's normal network controls, and test with a non-production program number first. -n mode embeds a particular transport identifier and is therefore site-specific.
Warning
Generated services wait 120 seconds after servicing a request by default. Use -K 0 if the service should exit immediately after a request, or -K -1 if it should never exit. Only use -K -1 for a service started by a port monitor whose process model actually requires it, and check the generated service against its launcher before changing this value.
Common traps and recovery
- Old output survives a regeneration. rpcgen writes named files and happily overwrites them. Move an experimental directory aside and regenerate into a clean one, or restore overwritten hand edits from version control. Generated files are disposable; hand-written service routines are not.
- Multiple procedure arguments look rejected or awkward. Use
-Nfor the new style, which permits multiple arguments and passes them in a C-like way. It is not the default because old generated interfaces still use the historical style. - A nested structure will not compile. The RPC language does not support nested structures. Declare the inner structure at top level and reference its name from the containing one.
- Generated output differs between machines. Record the rpcsvc-proto version and the relevant
NETPATHor/etc/netconfiginputs; transport selection is part of the build environment, not just the .x file.
To undo this walkthrough, remove only the private demo directory once you have confirmed it holds no useful work. Under version control, use the repository's normal restore process for generated files and review the diff before committing. Never remove a shared source directory or a production-generated service as part of cleanup.
Done means
- Version recorded.
rpcgen --versionreports the rpcsvc-proto build you intend to use. - Names are unique. Your
.xfile has distinct program, version, procedure and type names. - Symbols check out. The generated header and stubs contain what you expect.
- C compiles or the gap is identified. A syntax-only check passed, or the missing development dependency is named.
- Deployment choices are written down. Transport, inetd, dispatch-table and
-Ksettings are documented before anything starts.