Writing distributed programs with Courier
Computer Science Division \(em EECS
This paper describes the implementation and use
of the Courier remote procedure call protocol for Berkeley UNIX
Courier is both a protocol and a specification language.
The protocol specifies how remote procedures are invoked and
how parameters and results of various data types are transmitted.
The specification language, somewhat reminiscent of Mesa,
provides a simple way of defining the remote interfaces of distributed
This document assumes familiarity with the Courier language,
details of which may be found in
the Courier protocol definition.\**
``Courier: The Remote Procedure Call Protocol.''
Xerox System Integration Standard 038112,
However, for reference purposes, a grammar for the Courier language
may be found in the appendix to this document.
The simplest form of distributed program using Courier
The first is the specification,
written in the Courier language.
The specification must declare all procedures of
the program that will be called remotely (the
.i "server procedures" ).
The next part is the implementation, in C, of the server procedures.
Finally, there is the client portion of the program, also in C, which calls
.sh 1 "The mapping between C and Courier"
The Courier specification language includes
facilities for declaring new types and constants of given
Certain of these types presuppose a programming language
environment capable of supporting them:
for example, there are error types that procedures may
report in lieu of returning a result,
and procedures may return multiple results.
Because of the lack of support for these features in the UNIX C
environment, they are not supported in this implementation.
The following typedefs correspond to predefined Courier types:
LongCardinal LONG CARDINAL
.sh 2 "Constructed types"
The Courier enumeration, array, and record types
correspond to C enumerations, arrays, and structures.
Procedures correspond to C functions,
except that error reports and multiple results are not supported.
Error types are not supported.
The Courier sequence and choice types
pose some problems when they are mapped into C.
This is because an object of one of these types
must contain run-time information (the length of the sequence
or which choice is present)
that is implicit in Courier, but must be made explicit
Furthermore, the C programmer must bear the responsibility of keeping
this information consistent.
A sequence type is mapped into a structure consisting
and a pointer to the sequence elements called
The consistency requirement is that
indicate the number of elements in the array pointed to
A choice type is mapped into a structure consisting
of an enumeration element called
and a union of all the possible choices.
The designator is of the enumeration type defined
(implicitly or explicitly)
in the declaration of the choice type.
If this enumeration consists of elements
then the choices are accessible as
The consistency requirement is that
contain the enumeration value corresponding to
which choice currently occupies the union.
Although the Courier language allows constants of
it is difficult to define constants of constructed types
Consequently, this implementation restricts constants
.sh 1 "How to build a Courier program"
The specification file is expected to have the extension
Consider the following skeletal Courier program definition:
Example : PROGRAM = BEGIN ... END.
The name of this Courier program is
by convention, this specification would stored in the file
The first step is to use the Courier compiler on the specification,
Assuming there are no errors in compilation,
the following files will have been produced:
Example.h definitions and typedefs
Example_stubs.c routines to map between C and Courier
Example_server.c server routines
Example_client.c client routines
in all user-written parts of the Courier program
(i.e., the implementations of the client program and server procedures.)
It is intended to be readable, so that
the user can refer to it directly rather than
memorize the correspondence between Courier types
and C types discussed above.
The user program may call server procedures on any number
although in the current implementation, only one remote interface
In order to activate a remote interface for a Courier program,
CourierActivate(machine_name, program_name);
Both parameters are strings.
The call returns 0 or -1 for success or failure, respectively.
Once the interface has been successfully activated,
the client program may call the server procedures exactly as if
they were local C functions. (The compiler produces stubs
which invoke the corresponding procedure on the remote machine.)
The client program should be loaded with Example_client.o
and -lcr (the Courier library) to produce an executable program.
The server procedures should be loaded with Example_server.o
and -lcr to produce the server process that will be invoked whenever
an activation request arrives.
.sh 1 "The Courier Daemon"
The Courier protocol specifies a standard for communicating
parameters and results which has been adhered to in this
It also specifies an initial connection protocol
and a format for messages which have been somewhat simplified.
There is a single Courier Daemon per machine whose function
is to listen on a well-known port for Courier interface activation
A request contains the name of the Courier program
whose server is to be activated.
The executable file for this program must reside in a special directory
(/usr/lib/courier) and have the same name as the Courier program it contains.
either spawns this executable file
or replies with an error message.
The format for messages has been simplified somewhat
because reject and abort messages are not used.
This section contains a sample Courier program,
which implements remote lookup in
(the UNIX database of user names, passwords, home directories, and so on.)
.sh 2 "The specification (PasswordLookup.cr)"
PasswordLookup : PROGRAM =
-- This is a translation of the passwd structure in <pwd.h>
pw_name, pw_passwd : STRING,
pw_uid, pw_gid, pw_quota : LONG CARDINAL,
pw_comment, pw_gecos, pw_dir, pw_shell : STRING
-- Given a user name, return a Passwd record.
LookupUser : PROCEDURE [user : STRING] RETURNS [passwd : Passwd]
-- Given a user id, return a Passwd record.
LookupUid : PROCEDURE [uid : CARDINAL] RETURNS [passwd : Passwd]
.sh 2 "The server procedures (PasswordLookup.c)"
#include "PasswordLookup.h"
extern Passwd *getpwnam(), *getpwuid();
Passwd empty = { "", "", 0, 0, 0, "", "" };
.sh 2 "The user program (lookup.c)"
* Sample program to access remote password lookup.
* Usage: lookup machine username
#include "PasswordLookup.h"
fprintf(stderr, "Usage: %s machine username\en", argv[0]);
if (CourierActivate(argv[1], "PasswordLookup") == 0) {
passwd = LookupUser(argv[2]);
if (strcmp(passwd.pw_name, argv[2]) != 0)
printf("User %s is unknown on %s.\en",
printf("%s:%s:%d:%d:%s:%s:%s\en",
USEROBJS = lookup.o PasswordLookup_client.o
SRVROBJS = PasswordLookup.o PasswordLookup_server.o
DESTDIR = /usr/lib/courier
all: lookup PasswordLookup
cc -o lookup $(USEROBJS) $(LIBS)
PasswordLookup: $(SRVROBJS)
cc -o PasswordLookup $(SRVROBJS) $(LIBS)
$(USEROBJS) $(SRVROBJS): PasswordLookup.h
PasswordLookup_server.c \e
PasswordLookup_client.c: PasswordLookup.cr
courier PasswordLookup.cr
install -s PasswordLookup $(DESTDIR)
-rm -f *.o PasswordLookup_*.c PasswordLookup.h
The program number and version number fields in a Courier program
specification are ignored, and instead the program name is used
to activate the remote interface.
It was felt that this more dynamic form of binding
was more in keeping with the UNIX environment than centrally administered
The DEPENDS UPON facility of the Courier language is not
An approximation to Courier error reporting might be
implemented using UNIX signals.
The issues of authentication and protection are
They are ignored in the Courier protocol definition
and in this implementation,
but must be addressed in order to make this a useful tool.
Currently, each Courier program must perform its own authentification
On the other hand, the daemon spawns all server processes as root
(i.e., with super-user privileges.)
For this reason, Courier programs should be "certified"
before being placed in /usr/lib/courier, and /usr/lib/courier
should be protected accordingly.
This appendix contains the grammar for the Courier language.
It is essentially identical to the YACC specification used
%token identifier number string
ARRAY BEGIN BOOLEAN CARDINAL
PROCEDURE PROGRAM RECORD REPORTS
RETURNS SEQUENCE STRING TRUE
TYPE UNSPECIFIED UPON VERSION
identifier ':' PROGRAM number VERSION number '='
BEGIN DependencyList DeclarationList END '.'
identifier ':' PROGRAM '='
BEGIN DependencyList DeclarationList END '.'
| DEPENDS UPON ReferencedProgramList ';'
| ReferencedProgramList ',' ReferencedProgram
identifier '(' number ')' VERSION number
| DeclarationList Declaration
identifier ':' TYPE '=' Type ';'
| identifier ':' Type '=' Constant ';'
'{' CorrespondenceList '}'
| ARRAY NumericValue OF Type
| SEQUENCE MaximumNumber OF Type
| RECORD '[' FieldList ']'
| CHOICE DesignatorType OF '{' CandidateList '}'
| PROCEDURE ArgumentList ResultList ErrorList
| identifier '.' identifier
| CorrespondenceList ',' Correspondence
identifier '(' NumericValue ')'
| CandidateList ',' Candidate
DesignatorList '=''>' Type
| DesignatorList ',' Designator
| RETURNS '[' FieldList ']'
| REPORTS '[' NameList ']'
| identifier '.' identifier
| ElementList ',' Constant
| ComponentList ',' Component
| NameList ',' identifier