4.1cBSD/usr/doc/courier/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
.)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
.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
.sp
	courier Example.cr
.sp
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.
.pp
The user program may call server procedures on any number
of other machines,
although in the current implementation, only one remote interface
may be active at a time.
In order to activate a remote interface for a Courier program,
the call
.sp
	CourierActivate(machine_name, program_name);
.sp
must be given.
Both parameters are strings.
The call returns 0 or -1 for success or failure, respectively.
.pp
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.)
.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 which have been somewhat simplified.
.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 sample 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);
	}
	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",
				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 might be
implemented using UNIX signals.
.lp
The issues of authentication and protection are
difficult.
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
if this is desired.
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.
.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
.nf
%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
%%

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
	;

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