|
|
1.1 root 1: .fo ''%''
2: .(l C
3: .ps +4
4: .b
5: Writing Distributed Programs with Courier
6: .ps -4
7: .sp 2
8: .i
9: Eric C. Cooper
10: .sp 2
11: .r
12: Computer Science Division \(em EECS
13: University of California
14: Berkeley, CA 94720
15: .sp 2
16: March, 1982
17: .)l
18: .sh 1 "Introduction"
19: .pp
20: This paper describes the implementation and use
21: of the Courier remote procedure call protocol for Berkeley UNIX
22: (version 4.2).
23: Courier is both a protocol and a specification language.
24: The protocol specifies how remote procedures are invoked and
25: how parameters and results of various data types are transmitted.
26: The specification language, somewhat reminiscent of Mesa,
27: provides a simple way of defining the remote interfaces of distributed
28: programs.
29: This document assumes familiarity with the Courier language,
30: details of which may be found in
31: the Courier protocol definition.\**
32: .r
33: .(f
34: \**
35: ``Courier: The Remote Procedure Call Protocol.''
36: Xerox System Integration Standard 038112,
37: December 1981.
38: .)f
39: However, for reference purposes, a grammar for the Courier language
40: may be found in the appendix to this document.
41: .pp
42: The simplest form of distributed program using Courier
43: consists of three parts.
44: The first is the specification,
45: written in the Courier language.
46: The specification must declare all procedures of
47: the program that will be called remotely (the
48: .i "server procedures" ).
49: The next part is the implementation, in C, of the server procedures.
50: Finally, there is the client portion of the program, also in C, which calls
51: the server procedures.
52: .sh 1 "The mapping between C and Courier"
53: .pp
54: The Courier specification language includes
55: facilities for declaring new types and constants of given
56: types.
57: Certain of these types presuppose a programming language
58: environment capable of supporting them:
59: for example, there are error types that procedures may
60: report in lieu of returning a result,
61: and procedures may return multiple results.
62: Because of the lack of support for these features in the UNIX C
63: environment, they are not supported in this implementation.
64: .sh 2 "Predefined types"
65: .pp
66: The following typedefs correspond to predefined Courier types:
67: .(b
68: .TS
69: c c
70: l l.
71: C typedef Courier type
72:
73: Boolean BOOLEAN
74: Cardinal CARDINAL
75: LongCardinal LONG CARDINAL
76: Integer INTEGER
77: LongInteger LONG INTEGER
78: String STRING
79: Unspecified UNSPECIFIED
80: LongUnspecified LONG UNSPECIFIED
81: .TE
82: .)b
83: .sh 2 "Constructed types"
84: .pp
85: The Courier enumeration, array, and record types
86: correspond to C enumerations, arrays, and structures.
87: .pp
88: Procedures correspond to C functions,
89: except that error reports and multiple results are not supported.
90: Error types are not supported.
91: .pp
92: The Courier sequence and choice types
93: pose some problems when they are mapped into C.
94: This is because an object of one of these types
95: must contain run-time information (the length of the sequence
96: or which choice is present)
97: that is implicit in Courier, but must be made explicit
98: in C.
99: Furthermore, the C programmer must bear the responsibility of keeping
100: this information consistent.
101: .pp
102: A sequence type is mapped into a structure consisting
103: of a Cardinal called
104: .i "length" ,
105: and a pointer to the sequence elements called
106: .i "sequence" .
107: The consistency requirement is that
108: .i "length"
109: indicate the number of elements in the array pointed to
110: by
111: .i "sequence" .
112: .pp
113: A choice type is mapped into a structure consisting
114: of an enumeration element called
115: .i "designator" ,
116: and a union of all the possible choices.
117: The designator is of the enumeration type defined
118: (implicitly or explicitly)
119: in the declaration of the choice type.
120: If this enumeration consists of elements
121: .i A ,
122: .i B ,
123: and
124: .i C ,
125: then the choices are accessible as
126: .i A_case ,
127: .i B_case ,
128: and
129: .i C_case .
130: The consistency requirement is that
131: .i "designator"
132: contain the enumeration value corresponding to
133: which choice currently occupies the union.
134: .sh 2 "Constants"
135: .pp
136: Although the Courier language allows constants of
137: any type to be declared,
138: it is difficult to define constants of constructed types
139: in C programs.
140: Consequently, this implementation restricts constants
141: to be numeric.
142: .sh 1 "How to build a Courier program"
143: .pp
144: The specification file is expected to have the extension
145: .i ".cr" .
146: Consider the following skeletal Courier program definition:
147: .sp
148: Example : PROGRAM = BEGIN ... END.
149: .sp
150: The name of this Courier program is
151: .i "Example" ;
152: by convention, this specification would stored in the file
153: .i "Example.cr" .
154: .pp
155: The first step is to use the Courier compiler on the specification,
156: by doing either
157: .sp
158: courier Example.cr
159: .sp
160: or
161: .sp
162: courier -x Example.cr
163: .sp
164: (The
165: .i "-x"
166: option is explained below.)
167: Assuming there are no errors in compilation,
168: the following files will have been produced:
169: .(b
170: .TS
171: c c
172: l l.
173: File Contents
174:
175: Example.h definitions and typedefs
176: Example_stubs.c routines to map between C and Courier
177: Example_server.c server routines
178: Example_client.c client routines
179: .TE
180: .)b
181: .pp
182: The header file
183: .i "Example.h"
184: should be included via
185: .sp
186: #include ``Example.h''
187: .sp
188: in all user-written parts of the Courier program
189: (i.e., the implementations of the client program and server procedures.)
190: It is intended to be readable, so that
191: the user can refer to it directly rather than
192: memorize the correspondence between Courier types
193: and C types discussed above.
194: .sh 2 "Calling remote procedures"
195: .pp
196: The current implementation of Courier supports two styles of
197: .i binding
198: remote program interfaces to the user program:
199: a per-session mechanism (the default) and a per-call mechanism,
200: which is requested by giving the Courier compiler the
201: .i "-x"
202: option (for
203: .i explicit
204: binding.)
205: .pp
206: If the per-session mechanism is used, only one instance of each remote interface
207: may be active at a time
208: (although several different remote programs may be used
209: simultaneously.)
210: The remote interface for the ``Example'' program above
211: is activated by the call
212: .sp
213: BindExampleToMachine(machine_name);
214: .sp
215: (Note that the name of the function to be called depends on the Courier
216: program name; it is generated automatically by the Courier compiler.)
217: Subsequent calls will deactivate any existing interface for the program before
218: activating a new one.
219: Once an interface is activated, the remote procedures in it may be called
220: exactly as if they were local C functions.
221: (The compiler produces stubs
222: which invoke the corresponding procedures on the remote machine.)
223: .pp
224: To use the explicit, or per-call, binding mechanism, the
225: .i "-x"
226: option must be given to the compiler, as mentioned above.
227: No binding call need be given in the user program;
228: instead,
229: each remote procedure to be called must be passed
230: .i "an additional parameter" .
231: This parameter, a string, is the machine name
232: on which the procedure should be executed;
233: it precedes any other parameters declared in the Courier specification.
234: (Again, the compiler produces the appropriate stubs,
235: this time with the additional parameter,
236: which invoke the corresponding procedures on the specified remote machine.)
237: .pp
238: This style of binding allows any number of instances of the same
239: remote interface to be used.
240: For instance, one could (inefficiently) implement a third-party file transfer
241: by reading from one file server and writing to a second file server.
242: Since both the read and write procedures would presumably be defined in
243: the same Courier specification, per-session binding could not be used.\**
244: .(f
245: \**
246: I am indebted to Jeff Mogul for this example.
247: .)f
248: .pp
249: The client program should be loaded with Example_client.o
250: and -lcr (the Courier library) to produce an executable program.
251: The server procedures should be loaded with Example_server.o
252: and -lcr to produce the server process that will be invoked whenever
253: an activation request arrives.
254: .sh 1 "The Courier Daemon"
255: .pp
256: The Courier protocol specifies a standard for communicating
257: parameters and results which has been adhered to in this
258: implementation.
259: It also specifies an initial connection protocol
260: and a format for messages, both of which have been somewhat simplified
261: for the UNIX environment.
262: .pp
263: There is a single Courier Daemon per machine whose function
264: is to listen on a well-known port for Courier interface activation
265: requests.
266: A request contains the name of the Courier program
267: whose server is to be activated.
268: The executable file for this program must reside in a special directory
269: (/usr/lib/courier) and have the same name as the Courier program it contains.
270: The daemon
271: either spawns this executable file
272: or replies with an error message.
273: .pp
274: The format for messages has been simplified somewhat
275: because reject and abort messages are not used.
276: .sh 1 "An example"
277: .pp
278: This section contains a Courier program
279: which implements remote lookup in
280: .i "/etc/passwd"
281: (the UNIX database of user names, passwords, home directories, and so on.)
282: .bp
283: .sh 2 "The specification (PasswordLookup.cr)"
284: .sp 2
285: .nf
286: PasswordLookup : PROGRAM =
287:
288: BEGIN
289:
290: -- This is a translation of the passwd structure in <pwd.h>
291:
292: Passwd : TYPE = RECORD [
293: pw_name, pw_passwd : STRING,
294: pw_uid, pw_gid, pw_quota : LONG CARDINAL,
295: pw_comment, pw_gecos, pw_dir, pw_shell : STRING
296: ];
297:
298: -- Remote entry points.
299:
300: -- Given a user name, return a Passwd record.
301:
302: LookupUser : PROCEDURE [user : STRING] RETURNS [passwd : Passwd]
303: = 0;
304:
305: -- Given a user id, return a Passwd record.
306:
307: LookupUid : PROCEDURE [uid : CARDINAL] RETURNS [passwd : Passwd]
308: = 1;
309: END.
310: .fi
311: .bp
312: .sh 2 "The server procedures (PasswordLookup.c)"
313: .sp 2
314: .nf
315: #include <stdio.h>
316: #include "PasswordLookup.h"
317:
318: extern Passwd *getpwnam(), *getpwuid();
319:
320: Passwd empty = { "", "", 0, 0, 0, "", "" };
321:
322: Passwd
323: LookupUser(user)
324: String user;
325: {
326: Passwd *pw;
327:
328: pw = getpwnam(user);
329: if (pw == 0)
330: return (empty);
331: else
332: return (*pw);
333: }
334:
335: Passwd
336: LookupUid(uid)
337: Cardinal uid;
338: {
339: Passwd *pw;
340:
341: pw = getpwuid(uid);
342: if (pw == 0)
343: return (empty);
344: else
345: return (*pw);
346: }
347: .fi
348: .bp
349: .sh 2 "The user program (lookup.c)"
350: .sp 2
351: .nf
352: /*
353: * Sample program to access remote password lookup.
354: *
355: * Usage: lookup machine username
356: */
357: #include <stdio.h>
358: #include "PasswordLookup.h"
359:
360: main(argc, argv)
361: int argc;
362: char **argv;
363: {
364: Passwd passwd;
365:
366: if (argc != 3) {
367: fprintf(stderr, "Usage: %s machine username\en", argv[0]);
368: exit(1);
369: }
370: BindPasswordLookupToMachine(argv[1]);
371: passwd = LookupUser(argv[2]);
372: if (strcmp(passwd.pw_name, argv[2]) != 0)
373: printf("User %s is unknown on %s.\en",
374: argv[2], argv[1]);
375: else
376: display(&passwd);
377: }
378:
379: display(p)
380: Passwd *p;
381: {
382: printf("%s:%s:%d:%d:%s:%s:%s\en",
383: p->pw_name,
384: p->pw_passwd,
385: p->pw_uid,
386: p->pw_gid,
387: p->pw_gecos,
388: p->pw_dir,
389: p->pw_shell);
390: }
391: .fi
392: .bp
393: .sh 2 "Makefile"
394: .sp 2
395: .nf
396: CFLAGS = -O
397: USEROBJS = lookup.o PasswordLookup_client.o
398: SRVROBJS = PasswordLookup.o PasswordLookup_server.o
399: LIBS = -lcr
400: DESTDIR = /usr/lib/courier
401:
402: all: lookup PasswordLookup
403:
404: lookup: $(USEROBJS)
405: cc -o lookup $(USEROBJS) $(LIBS)
406:
407: PasswordLookup: $(SRVROBJS)
408: cc -o PasswordLookup $(SRVROBJS) $(LIBS)
409:
410: $(USEROBJS) $(SRVROBJS): PasswordLookup.h
411:
412: PasswordLookup.h \e
413: PasswordLookup_server.c \e
414: PasswordLookup_client.c: PasswordLookup.cr
415: courier PasswordLookup.cr
416:
417: install: all
418: install -s PasswordLookup $(DESTDIR)
419:
420: clean:
421: -rm -f *.o PasswordLookup_*.c PasswordLookup.h
422: .fi
423: .sh 1 "Final notes"
424: .lp
425: The program number and version number fields in a Courier program
426: specification are ignored, and instead the program name is used
427: to activate the remote interface.
428: It was felt that this more dynamic form of binding
429: was more in keeping with the UNIX environment than centrally administered
430: program numbers.
431: .lp
432: The DEPENDS UPON facility of the Courier language is not
433: supported.
434: .lp
435: An approximation to Courier error reporting has been designed,
436: using a combination of UNIX signals and the C non-local return
437: mechanism (a stack frame unwinding mechanism),
438: but this has not yet been implemented.
439: .lp
440: The issues of authentication and protection are
441: difficult.
442: They are only touched upon in this implementation,
443: by making the Courier daemon spawn each server process with privileges
444: equivalent to the protection of the corresponding executable file in
445: /usr/lib/courier.
446: Currently, each Courier program must perform any further authentification
447: if this is desired.
448: .sh 1 "Appendix"
449: .pp
450: This appendix contains the grammar for the Courier language.
451: It is essentially identical to the YACC specification used
452: by the Courier compiler.
453: .sp
454: .ps -2p
455: .vs -2p
456: .nf
457: .TS
458: l l l l l.
459: %token identifier number string
460: ARRAY BEGIN BOOLEAN CARDINAL
461: CHOICE DEPENDS END ERROR
462: FALSE INTEGER LONG OF
463: PROCEDURE PROGRAM RECORD REPORTS
464: RETURNS SEQUENCE STRING TRUE
465: TYPE UNSPECIFIED UPON VERSION
466: .TE
467: %%
468:
469: Program :
470: identifier ':' PROGRAM number VERSION number '='
471: BEGIN DependencyList DeclarationList END '.'
472: |
473: identifier ':' PROGRAM '='
474: BEGIN DependencyList DeclarationList END '.'
475: ;
476:
477: DependencyList :
478: /* empty */
479: | DEPENDS UPON ReferencedProgramList ';'
480: ;
481:
482: ReferencedProgramList :
483: ReferencedProgram
484: | ReferencedProgramList ',' ReferencedProgram
485: ;
486:
487: ReferencedProgram :
488: identifier '(' number ')' VERSION number
489: ;
490:
491: DeclarationList :
492: /* empty */
493: | DeclarationList Declaration
494: ;
495:
496: Declaration :
497: identifier ':' TYPE '=' Type ';'
498: | identifier ':' Type '=' Constant ';'
499: ;
500:
501: Type :
502: PredefinedType
503: | ConstructedType
504: | ReferencedType
505: ;
506:
507: PredefinedType :
508: BOOLEAN
509: | CARDINAL
510: | LONG CARDINAL
511: | INTEGER
512: | LONG INTEGER
513: | STRING
514: | UNSPECIFIED
515: | LONG UNSPECIFIED
516: ;
517:
518: ConstructedType :
519: '{' CorrespondenceList '}'
520: | ARRAY NumericValue OF Type
521: | SEQUENCE MaximumNumber OF Type
522: | RECORD '[' FieldList ']'
523: | RECORD '[' ']'
524: | CHOICE DesignatorType OF '{' CandidateList '}'
525: | PROCEDURE ArgumentList ResultList ErrorList
526: | ERROR ArgumentList
527: ;
528:
529: ReferencedType :
530: identifier
531: | identifier '.' identifier
532: ;
533:
534: CorrespondenceList :
535: Correspondence
536: | CorrespondenceList ',' Correspondence
537: ;
538:
539: Correspondence :
540: identifier '(' NumericValue ')'
541: ;
542:
543: MaximumNumber :
544: NumericValue
545: | /* empty */
546: ;
547:
548: NumericValue :
549: number
550: | ReferencedConstant
551: ;
552:
553: DesignatorType :
554: /* empty */
555: | ReferencedType
556: ;
557:
558: CandidateList :
559: Candidate
560: | CandidateList ',' Candidate
561: ;
562:
563: Candidate :
564: DesignatorList '=''>' Type
565: ;
566:
567: DesignatorList :
568: Designator
569: | DesignatorList ',' Designator
570: ;
571:
572: Designator :
573: identifier
574: | Correspondence
575: ;
576:
577: ArgumentList :
578: /* empty */
579: | '[' FieldList ']'
580: ;
581:
582: ResultList :
583: /* empty */
584: | RETURNS '[' FieldList ']'
585: ;
586:
587: ErrorList :
588: /* empty */
589: | REPORTS '[' NameList ']'
590: ;
591:
592: FieldList :
593: Field
594: | FieldList ',' Field
595: ;
596:
597: Field :
598: NameList ':' Type
599: ;
600:
601: Constant :
602: PredefinedConstant
603: | ConstructedConstant
604: | ReferencedConstant
605: ;
606:
607: PredefinedConstant :
608: TRUE
609: | FALSE
610: | number
611: | '-' number
612: | '"' string '"'
613: ;
614:
615: ConstructedConstant :
616: identifier
617: | '[' ElementList ']'
618: | '[' ComponentList ']'
619: | '['']'
620: | identifier Constant
621: | number
622: ;
623:
624: ReferencedConstant :
625: identifier
626: | identifier '.' identifier
627: ;
628:
629: ElementList :
630: Constant
631: | ElementList ',' Constant
632: ;
633:
634: ComponentList :
635: Component
636: | ComponentList ',' Component
637: ;
638:
639: Component :
640: NameList ':' Constant
641: ;
642:
643: NameList :
644: identifier
645: | NameList ',' identifier
646: ;
647: .fi
648: .vs
649: .ps
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.