4.3BSD/usr/contrib/courier/doc/courier.tbl.me

Compare this file to the similar file:
Show the results in this format:

.fo ''%''
.(l C
.ps +4
.b
Writing Distributed Programs with Courier
.ps -4
.sp 2
.i
Eric C. Cooper
.sp 2
.r
Computer Science Division \(em EECS
University of California
Berkeley, CA  94720
.sp 2
March, 1982
.)l
.sh 1 "Introduction"
.pp
This paper describes the implementation and use
of the Courier remote procedure call protocol for Berkeley UNIX
(version 4.2).
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
programs.
This document assumes familiarity with the Courier language,
details of which may be found in
the Courier protocol definition.\**
.r
.(f
\**
``Courier: The Remote Procedure Call Protocol.''
Xerox System Integration Standard 038112,
December 1981.
.)f
However, for reference purposes, a grammar for the Courier language
may be found in the appendix to this document.
.pp
The simplest form of distributed program using Courier
consists of three parts.
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
the server procedures.
.sh 1 "The mapping between C and Courier"
.pp
The Courier specification language includes
facilities for declaring new types and constants of given
types.
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.
.sh 2 "Predefined types"
.pp
The following typedefs correspond to predefined Courier types:
.(b
.TS
c c
l l.
C typedef	Courier type

Boolean	BOOLEAN
Cardinal	CARDINAL
LongCardinal	LONG CARDINAL
Integer	INTEGER
LongInteger	LONG INTEGER
String	STRING
Unspecified	UNSPECIFIED
LongUnspecified	LONG UNSPECIFIED
.TE
.)b
.sh 2 "Constructed types"
.pp
The Courier enumeration, array, and record types
correspond to C enumerations, arrays, and structures.
.pp
Procedures correspond to C functions,
except that error reports and multiple results are not supported.
Error types are not supported.
.pp
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
in C.
Furthermore, the C programmer must bear the responsibility of keeping
this information consistent.
.pp
A sequence type is mapped into a structure consisting
of a Cardinal called
.i "length" ,
and a pointer to the sequence elements called
.i "sequence" .
The consistency requirement is that
.i "length"
indicate the number of elements in the array pointed to
by
.i "sequence" .
.pp
A choice type is mapped into a structure consisting
of an enumeration element called
.i "designator" ,
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
.i A ,
.i B ,
and
.i C ,
then the choices are accessible as
.i A_case ,
.i B_case ,
and
.i C_case .
The consistency requirement is that
.i "designator"
contain the enumeration value corresponding to
which choice currently occupies the union.
.sh 2 "Constants"
.pp
Although the Courier language allows constants of
any type to be declared,
it is difficult to define constants of constructed types
in C programs.
Consequently, this implementation restricts constants
to be numeric.
.sh 1 "How to build a Courier program"
.pp
The specification file is expected to have the extension
.i ".cr" .
Consider the following skeletal Courier program definition:
.sp
	Example : PROGRAM = BEGIN ... END.
.sp
The name of this Courier program is
.i "Example" ;
by convention, this specification would stored in the file
.i "Example.cr" .
.pp
The first step is to use the Courier compiler on the specification,
by doing either
.sp
	courier Example.cr
.sp
or
.sp
	courier -x Example.cr
.sp
(The
.i "-x"
option is explained below.)
Assuming there are no errors in compilation,
the following files will have been produced:
.(b
.TS
c c
l l.
File	Contents

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
.TE
.)b
.pp
The header file
.i "Example.h"
should be included via
.sp
	#include ``Example.h''
.sp
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.
.sh 2 "Calling remote procedures"
.pp
The current implementation of Courier supports two styles of
.i binding
remote program interfaces to the user program:
a per-session mechanism (the default) and a per-call mechanism,
which is requested by giving the Courier compiler the
.i "-x"
option (for
.i explicit
binding.)
.pp
If the per-session mechanism is used, only one instance of each remote interface
may be active at a time
(although several different remote programs may be used
simultaneously.)
The remote interface for the ``Example'' program above
is activated by the call
.sp
	BindExampleToMachine(machine_name);
.sp
(Note that the name of the function to be called depends on the Courier
program name; it is generated automatically by the Courier compiler.)
Subsequent calls will deactivate any existing interface for the program before
activating a new one.
Once an interface is activated, the remote procedures in it may be called
exactly as if they were local C functions.
(The compiler produces stubs
which invoke the corresponding procedures on the remote machine.)
.pp
To use the explicit, or per-call, binding mechanism, the
.i "-x"
option must be given to the compiler, as mentioned above.
No binding call need be given in the user program;
instead,
each remote procedure to be called must be passed
.i "an additional parameter" .
This parameter, a string, is the machine name 
on which the procedure should be executed;
it precedes any other parameters declared in the Courier specification.
(Again, the compiler produces the appropriate stubs,
this time with the additional parameter,
which invoke the corresponding procedures on the specified remote machine.)
.pp
This style of binding allows any number of instances of the same 
remote interface to be used.
For instance, one could (inefficiently) implement a third-party file transfer
by reading from one file server and writing to a second file server.
Since both the read and write procedures would presumably be defined in
the same Courier specification, per-session binding could not be used.\**
.(f
\**
I am indebted to Jeff Mogul for this example.
.)f
.pp
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"
.pp
The Courier protocol specifies a standard for communicating
parameters and results which has been adhered to in this
implementation.
It also specifies an initial connection protocol
and a format for messages, both of which have been somewhat simplified
for the UNIX environment.
.pp
There is a single Courier Daemon per machine whose function
is to listen on a well-known port for Courier interface activation
requests.
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.
The daemon
either spawns this executable file
or replies with an error message.
.pp
The format for messages has been simplified somewhat
because reject and abort messages are not used.
.sh 1 "An example"
.pp
This section contains a Courier program
which implements remote lookup in
.i "/etc/passwd"
(the UNIX database of user names, passwords, home directories, and so on.)
.bp
.sh 2 "The specification (PasswordLookup.cr)"
.sp 2
.nf
    PasswordLookup : PROGRAM =

    BEGIN

	-- This is a translation of the passwd structure in <pwd.h>

	Passwd : TYPE = RECORD [
	    pw_name, pw_passwd : STRING,
	    pw_uid, pw_gid, pw_quota : LONG CARDINAL,
	    pw_comment, pw_gecos, pw_dir, pw_shell : STRING
	];

	-- Remote entry points.

	-- Given a user name, return a Passwd record.

	LookupUser : PROCEDURE [user : STRING] RETURNS [passwd : Passwd]
			= 0;

	-- Given a user id, return a Passwd record.

	LookupUid : PROCEDURE [uid : CARDINAL] RETURNS [passwd : Passwd]
			= 1;
    END.
.fi
.bp
.sh 2 "The server procedures (PasswordLookup.c)"
.sp 2
.nf
#include <stdio.h>
#include "PasswordLookup.h"

extern Passwd *getpwnam(), *getpwuid();

Passwd empty = { "", "", 0, 0, 0, "", "" };

Passwd
LookupUser(user)
	String user;
{
	Passwd *pw;

	pw = getpwnam(user);
	if (pw == 0)
		return (empty);
	else
		return (*pw);
}

Passwd
LookupUid(uid)
	Cardinal uid;
{
	Passwd *pw;

	pw = getpwuid(uid);
	if (pw == 0)
		return (empty);
	else
		return (*pw);
}
.fi
.bp
.sh 2 "The user program (lookup.c)"
.sp 2
.nf
/*
 * Sample program to access remote password lookup.
 *
 * Usage: lookup machine username
 */
#include <stdio.h>
#include "PasswordLookup.h"

main(argc, argv)
	int argc;
	char **argv;
{
	Passwd passwd;

	if (argc != 3) {
		fprintf(stderr, "Usage: %s machine username\en", argv[0]);
		exit(1);
	}
	BindPasswordLookupToMachine(argv[1]);
	passwd = LookupUser(argv[2]);
	if (strcmp(passwd.pw_name, argv[2]) != 0)
		printf("User %s is unknown on %s.\en",
			argv[2], argv[1]);
	else
		display(&passwd);
}

display(p)
	Passwd *p;
{
	printf("%s:%s:%d:%d:%s:%s:%s\en",
		p->pw_name,
		p->pw_passwd,
		p->pw_uid,
		p->pw_gid,
		p->pw_gecos,
		p->pw_dir,
		p->pw_shell);
}
.fi
.bp
.sh 2 "Makefile"
.sp 2
.nf
CFLAGS = -O
USEROBJS = lookup.o PasswordLookup_client.o
SRVROBJS = PasswordLookup.o PasswordLookup_server.o
LIBS = -lcr
DESTDIR = /usr/lib/courier

all:	lookup PasswordLookup

lookup:	$(USEROBJS)
	cc -o lookup $(USEROBJS) $(LIBS)

PasswordLookup:	$(SRVROBJS)
	cc -o PasswordLookup $(SRVROBJS) $(LIBS)

$(USEROBJS) $(SRVROBJS):	PasswordLookup.h

PasswordLookup.h \e
PasswordLookup_server.c \e
PasswordLookup_client.c:	PasswordLookup.cr
	courier PasswordLookup.cr

install:	all
	install -s PasswordLookup $(DESTDIR)

clean:
	-rm -f *.o PasswordLookup_*.c PasswordLookup.h
.fi
.sh 1 "Final notes"
.lp
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
program numbers.
.lp
The DEPENDS UPON facility of the Courier language is not
supported.
.lp
An approximation to Courier error reporting has been designed,
using a combination of UNIX signals and the C non-local return
mechanism (a stack frame unwinding mechanism),
but this has not yet been implemented.
.lp
The issues of authentication and protection are
difficult.
They are only touched upon in this implementation,
by making the Courier daemon spawn each server process with privileges
equivalent to the protection of the corresponding executable file in
/usr/lib/courier.
Currently, each Courier program must perform any further authentification
if this is desired.
.sh 1 "Appendix"
.pp
This appendix contains the grammar for the Courier language.
It is essentially identical to the YACC specification used
by the Courier compiler.
.sp
.ps -2p
.vs -2p
.nf
.TS
l l l l l.
%token	identifier	number	string	
	ARRAY	BEGIN	BOOLEAN	CARDINAL
	CHOICE	DEPENDS	END	ERROR
	FALSE	INTEGER	LONG	OF
	PROCEDURE	PROGRAM	RECORD	REPORTS
	RETURNS	SEQUENCE	STRING	TRUE
	TYPE	UNSPECIFIED	UPON	VERSION
.TE
%%

Program :
		identifier ':' PROGRAM number VERSION number '='
		BEGIN DependencyList DeclarationList END '.'
	|
		identifier ':' PROGRAM '='
		BEGIN DependencyList DeclarationList END '.'
	;

DependencyList :
		/* empty */
	|	DEPENDS UPON ReferencedProgramList ';'
	;

ReferencedProgramList :
		ReferencedProgram
	|	ReferencedProgramList ',' ReferencedProgram
	;

ReferencedProgram :
		identifier '(' number ')' VERSION number
	;

DeclarationList :
		/* empty */
	|	DeclarationList Declaration
	;

Declaration :
		identifier ':' TYPE '=' Type ';'
	|	identifier ':' Type '=' Constant ';'
	;

Type :
		PredefinedType
	|	ConstructedType
	|	ReferencedType
	;

PredefinedType :
		BOOLEAN
	|	CARDINAL
	|	LONG CARDINAL
	|	INTEGER
	|	LONG INTEGER
	|	STRING
	|	UNSPECIFIED
	|	LONG UNSPECIFIED
	;

ConstructedType :
		'{' CorrespondenceList '}'
	|	ARRAY NumericValue OF Type
	|	SEQUENCE MaximumNumber OF Type
	|	RECORD '[' FieldList ']'
	|	RECORD '[' ']'
	|	CHOICE DesignatorType OF '{' CandidateList '}'
	|	PROCEDURE ArgumentList ResultList ErrorList
	|	ERROR ArgumentList
	;

ReferencedType :
		identifier
	|	identifier '.' identifier
	;

CorrespondenceList :
		Correspondence
	|	CorrespondenceList ',' Correspondence
	;

Correspondence :
		identifier '(' NumericValue ')'
	;

MaximumNumber :
		NumericValue
	|	/* empty */
	;

NumericValue :
		number
	|	ReferencedConstant
	;

DesignatorType :
		/* empty */
	|	ReferencedType
	;

CandidateList :
		Candidate
	|	CandidateList ',' Candidate
	;

Candidate :
		DesignatorList '=''>' Type
	;

DesignatorList :
		Designator
	|	DesignatorList ',' Designator
	;

Designator :
		identifier
	|	Correspondence
	;

ArgumentList :
		/* empty */
	|	'[' FieldList ']'
	;

ResultList :
		/* empty */
	|	RETURNS '[' FieldList ']'
	;

ErrorList :
		/* empty */
	|	REPORTS '[' NameList ']'
	;

FieldList :
		Field
	|	FieldList ',' Field
	;

Field :
		NameList ':' Type
	;

Constant :
		PredefinedConstant
	|	ConstructedConstant
	|	ReferencedConstant
	;

PredefinedConstant :
		TRUE
	|	FALSE
	|	number
	|	'-' number
	|	'"' string '"'
	;

ConstructedConstant :
		identifier
	|	'[' ElementList ']'
	|	'[' ComponentList ']'
	|	'['']'
	|	identifier Constant
	|	number
	;

ReferencedConstant :
		identifier
	|	identifier '.' identifier
	;

ElementList :
		Constant
	|	ElementList ',' Constant
	;

ComponentList :
		Component
	|	ComponentList ',' Component
	;

Component :
		NameList ':' Constant
	;

NameList :
		identifier
	|	NameList ',' identifier
	;
.fi
.vs
.ps