Writing Distributed Programs with Courier Eric C. Cooper Computer Science Division - EECS University of California Berkeley, CA 94720 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 pro- grams. This document assumes familiarity with the Courier language, details of which may be found in the Courier pro- tocol definition.[1] [2] ``Courier: The Remote Procedure Call Protocol.'' Xerox System Integration Standard 038112, December 1981. 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 us- ing Courier consists of three parts. The first is the specification, written in the Courier language. The specif- ication must declare all procedures of the program that will be called remotely (the 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. The Courier specification language in- cludes facilities for declaring new types and constants of given types. Certain of these types presuppose a program- ming 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 multi- ple 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: c c l l. C typedef Courier type Boolean BOOLEAN Cardinal CARDINAL LongCardinal LONG CARDINAL Integer INTEGER LongInteger LONG INTEGER String STRING Unspecified UNSPECIFIED 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 informa- tion (the length of the sequence or which choice is present) July 31, 1987 - 2 - 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. A sequence type is mapped into a structure consisting of a Cardinal called and a pointer to the sequence elements called The consistency requirement is that indicate the number of elements in the array pointed to by 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 enumera- tion type defined (implicitly or explicitly) in the declara- tion of the choice type. If this enumeration consists of elements and then the choices are accessible as and The con- sistency requirement is that contain the enumeration value corresponding to which choice currently occupies the union. Although the Courier language allows constants of any type to be declared, it is difficult to define constants of con- structed types in C programs. Consequently, this implemen- tation restricts constants to be numeric. The specification file is expected to have the extension Consider the follow- ing 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, by doing courier Example.cr Assuming there are no errors in compilation, the following files will have been produced: 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 The header file should be included via #include ``Example.h'' in all user-written parts of the Courier program (i.e., the implementations of the client program and server pro- cedures.) It is intended to be readable, so that the user can refer to it directly rather than memorize the correspon- dence between Courier types and C types discussed above. 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 CourierActivate(machine_name, program_name); July 31, 1987 - 3 - must be given. 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 pro- gram. 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. 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. 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. The for- mat 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 direc- tories, and so on.) July 31, 1987 - 4 - PasswordLookup : PROGRAM = BEGIN -- This is a translation of the passwd structure in 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. July 31, 1987 - 5 - #include #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); } July 31, 1987 - 6 - /* * Sample program to access remote password lookup. * * Usage: lookup machine username */ #include #include "PasswordLookup.h" main(argc, argv) int argc; char **argv; { Passwd passwd; if (argc != 3) { fprintf(stderr, "Usage: %s machine username\n", 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.\n", argv[2], argv[1]); else display(&passwd); } } display(p) Passwd *p; { printf("%s:%s:%d:%d:%s:%s:%s\n", p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell); } July 31, 1987 - 7 - 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 \ PasswordLookup_server.c \ PasswordLookup_client.c: PasswordLookup.cr courier PasswordLookup.cr install: all install -s PasswordLookup $(DESTDIR) clean: -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 pro- gram numbers. The DEPENDS UPON facility of the Courier language is not supported. An approximation to Courier error reporting might be implemented using UNIX signals. 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. This appendix contains the grammar for the Courier language. It is essentially identical to the YACC specification used by the Courier compiler. %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 July 31, 1987 - 8 - %% 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 July 31, 1987 - 9 - | 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 ; July 31, 1987 - 10 - 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 July 31, 1987 - 11 - | ElementList ',' Constant ; ComponentList : Component | ComponentList ',' Component ; Component : NameList ':' Constant ; NameList : identifier | NameList ',' identifier ; July 31, 1987