|
|
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.