|
|
1.1 ! root 1: \" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $ ! 2: \" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $ ! 3: \" $Log: courier.tbl.me,v $ ! 4: \" Revision 1.8 87/03/17 09:32:52 ed ! 5: \" Added -I switch to establish search path for DEPENDS UPON files. ! 6: \" ! 7: \" Revision 1.7 86/11/22 07:12:05 jqj ! 8: \" small changes reflecting differences between 4.3bsd and 4.2 Maryland XNS. ! 9: \" ! 10: \" Revision 1.6 86/05/12 09:02:43 jqj ! 11: \" minor revisions ! 12: \" ! 13: \" Revision 1.5 85/03/26 06:30:58 jqj ! 14: \" Revised public alpha-test version, released 26 March 1985 ! 15: \" ! 16: \" Revision 1.4 85/03/11 16:46:01 jqj ! 17: \" Public alpha-test version, released 11 March 1985 ! 18: \" ! 19: .lg 0 ! 20: .fo ''%'' ! 21: .(l C ! 22: .ps +4 ! 23: .b ! 24: XNS Courier under UNIX (4.3BSD) ! 25: .ps -4 ! 26: .sp 2 ! 27: .r ! 28: .he $ XNS Courier $ $Date: 87/03/17 09:32:52 $ ! 29: J.Q. Johnson ! 30: Computer Science Department ! 31: Cornell University ! 32: 405 Upson Hall ! 33: Ithaca, NY 14853 ! 34: .)l ! 35: .sh 1 "Introduction" ! 36: .pp ! 37: This document describes the implementation and use ! 38: of the Courier remote procedure call protocol for Berkeley UNIX ! 39: (version 4.3), with XNS. ! 40: .sp 1 ! 41: .nf ! 42: .b ! 43: .(c ! 44: ******************************************************** ! 45: Warning: this document is a DRAFT design specification. ! 46: It has not yet been fully implemented, and the details of ! 47: the interface it specifies (particularly with respect to ! 48: Courier servers) are subject to change. Send design ! 49: comments to [email protected] or [email protected]. ! 50: ******************************************************** ! 51: .)c ! 52: .r ! 53: .fi ! 54: .sp 1 ! 55: .uh "Changes since previous revision:" ! 56: .np ! 57: Added discussion of encapsulated protocols. ! 58: .np ! 59: Added discussion of Clearinghouse library calls for finding a connection, ! 60: and relationship of connections to outstanding RPCs. ! 61: .uh "Earlier changes:" ! 62: .np ! 63: Changed names of generated file names to include version number. ! 64: Added discussion on scope of names vs. DEPENDS UPON modules. ! 65: .np ! 66: Users must now link Courier program ``Foo'' with Foo1_support.o. ! 67: .np ! 68: Added some discussion of freeing dynamic storage via clear_Foo. ! 69: .np ! 70: Changed ! 71: .i CourierOpen() ! 72: to take ns_addr structure rather than a string host name. Routines for ! 73: converting from name to address (using Clearinghouse) have not yet been ! 74: designed. ! 75: .np ! 76: Changed ERROR discussion to indicate that an ERROR is mapped to a long ! 77: with value > 65535. ! 78: .np ! 79: Changed description of BDT to reflect use of ! 80: .i BDTread() ! 81: and ! 82: .i BDTwrite() . ! 83: .np ! 84: Added SPPMAXDATA. ! 85: .np ! 86: Changed discussion of BulkData constants to indicate that things now ! 87: work correctly. ! 88: .sh 2 "General Description" ! 89: .pp ! 90: Courier is both a protocol and a specification language. ! 91: The protocol describes how remote procedures are invoked and ! 92: how parameters and results of various data types are transmitted. ! 93: The specification language, somewhat reminiscent of Mesa, ! 94: provides a simple way of defining the remote interfaces of distributed ! 95: programs. ! 96: .pp ! 97: This implementation is an attempt to allow UNIX C programmers to ! 98: construct distributed Courier programs to communicate with other ! 99: Courier implementations layered on XNS. It thus requires kernel ! 100: support for XNS Sequenced Packet Protocol sockets. ! 101: .pp ! 102: The simplest form of distributed program using Courier ! 103: consists of three parts. ! 104: The first is the specification, ! 105: written in the Courier language. ! 106: The specification must declare all procedures of ! 107: the program that will be called remotely (the ! 108: .i "server procedures" ). ! 109: The next part is the implementation, in C, of the server procedures. ! 110: Finally, there is the client portion of the program, also in C, which calls ! 111: the server procedures. ! 112: Note that either the client or server portion of the program may be ! 113: omitted if the UNIX program is designed to act with a non-UNIX ! 114: partner; for example, a simple printing client under UNIX might send ! 115: files to a Xerox laser printer using the Printing protocol published ! 116: and supported by Xerox. ! 117: .sh 2 "Acknowledgements" ! 118: .pp ! 119: Contributors to the current implementation have included ! 120: JQ Johnson, the primary implementor of the current version of the ! 121: Courier compiler under UNIX, ! 122: Eric Cooper, the author of the original UNIX Courier/TCP compiler, ! 123: Jeff Mogul, the author of the exception signalling mechanism used ! 124: here to handle abort and reject messages, ! 125: James O'Toole, Chris Torek, and Mark Weiser, ! 126: the authors of the 4.2BSD version of UNIX kernel support for XNS, ! 127: Keith Sklower, the author of the present (4.3BSD) version of UNIX kernel support, ! 128: Lee Moore, and Bill Nesheim. ! 129: .pp ! 130: Some of the software is copyright 1984 by Eric Cooper, and is for ! 131: research use only; it may be freely distributed but may not be ! 132: sold as a commercial product. ! 133: .sh 2 "References" ! 134: .pp ! 135: This document assumes familiarity with the Courier language, ! 136: details of which may be found in ! 137: the Courier protocol definition, ! 138: .i "Courier: The Remote Procedure Call Protocol" , ! 139: Xerox System Integration Standard 038112, ! 140: December 1981, and ``Appendix F, Bulk Data Transfer,'' Xerox System ! 141: Integration Standard 038112 Add. 1, October 1982. ! 142: However, for reference purposes, a grammar for the Courier language ! 143: may be found in the appendix to this document. ! 144: .pp ! 145: Users of Bulk Data Transfer will need to interact with SPP ! 146: (Sequenced Packet Protocol) ! 147: streams, and may wish to see ! 148: .i "Internet Transport Protocols" , ! 149: Xerox System Integration Standard 028112, December 1981. ! 150: .pp ! 151: Those interested in the workings of UNIX networking might consult ! 152: ``A 4.2bsd Interprocess Communications Primer,'' ! 153: (DRAFT of March 11, 1983), by Samuel J. Leffler, Robert S. Fabry, and ! 154: William N. Joy, and the ! 155: ``4.2BSD System Manual,'' ! 156: (Revised July, 1983), by William Joy ! 157: .i "et al" . ! 158: .pp ! 159: This document is also supplemented by various UNIX manual pages, notably ! 160: .i "xnscourier(3) " , ! 161: and ! 162: .i "except(3)" . ! 163: .sh 2 "Software and Hardware Dependencies" ! 164: .pp ! 165: This package requires the following software to operate: ! 166: .(l ! 167: 4.3BSD (currently tested only for VAX and Gould versions) ! 168: PCC, 4.3BSD version (probably\-limited testing has been done with Tartan C) ! 169: .)l ! 170: .pp ! 171: To communicate with Xerox products you will need an Ethernet ! 172: interface. Most of the existing example applications depend heavily on ! 173: the existence of a Xerox Clearinghouse somewhere on the local network. ! 174: .sh 1 "The Courier Specification Language" ! 175: .pp ! 176: The Courier specification language is documented (loosely) in the Xerox ! 177: ``Courier: The Remote Procedure Call Protocol.'' Our compiler ! 178: implements a large subset of the specification. Note that the embedding ! 179: of Courier constructs in a message or bit stream is usually irrelevant to the ! 180: applications programmer, who need only be concerned with the semantics ! 181: of the Courier datatypes. The courier compiler and runtimes translate ! 182: Courier datatypes into more easily managed C constructs and handle the ! 183: protocol of communication with the remote system. ! 184: .pp ! 185: This implementation places several restrictions on the specifications ! 186: it will accept: ! 187: .np ! 188: No forward references are permitted in the courier ! 189: specification. The user must reorder type specifications so that all ! 190: types and constants are defined before they are used. ! 191: Special provisions are made for recursive type declarations of the form ! 192: typically found in Bulk Data ``StreamOf'' declarations; see the ! 193: discussion of BDT below for details. General recursive declarations, ! 194: e.g. that of the Filing ``Filter,'' are not supported. ! 195: .np ! 196: The Courier specification is unclear as to lexical issues, ! 197: name scope, and name overloading. ! 198: In the current compiler, names should contain only upper and lower case ! 199: letters or digits. Also, all constants, types, and ! 200: enumeration tags within a module should have distinct names. Enumeration ! 201: tag names are currently global; thus, different enumeration types, even ! 202: in different modules, may not have the same tags. ! 203: .np ! 204: Constants of type CHOICE are not fully supported. In particular, they ! 205: are allowed only if the active arm of the choice is a nill record. ! 206: .np ! 207: Constants of type SEQUENCE are not supported. Constants of type ARRAY ! 208: (which have the same syntax as SEQUENCE constants) are supported, but are ! 209: not rangechecked; ``Vect: ARRAY 10 OF INTEGER = {1,2,3}'' is accepted, ! 210: though illegal. ! 211: .np ! 212: This implementation supports DEPENDS UPON and ! 213: referenced types and constants. However, the semantics of referenced ! 214: enumeration constants is unclear. Also, this implementation ! 215: does not support recursive dependencies. ! 216: .lp ! 217: We think we've eliminated all other restrictions, but we're probably wrong. ! 218: .sh 2 "Predefined types" ! 219: .pp ! 220: The following typedefs correspond to predefined Courier types: ! 221: .(b ! 222: .TS ! 223: c c c ! 224: l l l. ! 225: Courier type C typedef Implemented (on VAX) as ! 226: ! 227: BOOLEAN Boolean char (0==TRUE, 1==FALSE) ! 228: CARDINAL Cardinal unsigned short ! 229: LONG CARDINAL LongCardinal unsigned long ! 230: INTEGER Integer short ! 231: LONG INTEGER LongInteger long ! 232: STRING String char* ! 233: UNSPECIFIED Unspecified unsigned short ! 234: LONG UNSPECIFIED LongUnspecified unsigned long ! 235: .TE ! 236: .)b ! 237: Note that the representations of these types as seen by the applications ! 238: programmer are quite different from those sent over a Courier SPP connection. ! 239: For example, on the VAX a Cardinal's bytes are swapped before being ! 240: sent out in network byte order. A UNIX String corresponds to a pointer to ! 241: a null-terminated array of characters while a Courier serialized STRING ! 242: contains a count directly followed by an array of characters. Note ! 243: in particular that this implementation does ! 244: not support Courier STRINGs containing the ascii character NUL. ! 245: .sh 2 "Constructed types" ! 246: .pp ! 247: The Courier enumeration, array, and (non-null) record types ! 248: correspond to C enumerations, arrays, and structures respectively. ! 249: The Courier null record corresponds to an int. ! 250: .pp ! 251: The Courier sequence and choice types ! 252: pose some problems when they are mapped into C. ! 253: This is because an object of one of these types ! 254: must contain run-time information (the length of the sequence ! 255: or which choice is present) ! 256: that is implicit in Courier, but must be made explicit ! 257: in C. ! 258: Furthermore, the C programmer must bear the responsibility of keeping ! 259: this information consistent. ! 260: .pp ! 261: A sequence type is mapped into a structure consisting ! 262: of a Cardinal called ! 263: .i "length" , ! 264: and a pointer to the sequence elements called ! 265: .i "sequence" . ! 266: The consistency requirement is that ! 267: .i "length" ! 268: indicate the number of elements in the array pointed to ! 269: by ! 270: .i "sequence" . ! 271: .pp ! 272: A choice type is mapped into a structure consisting ! 273: of an enumeration element called ! 274: .i "designator" , ! 275: and a union of all the possible choices. ! 276: The designator is of the enumeration type defined ! 277: (implicitly or explicitly) ! 278: in the declaration of the choice type. ! 279: If this enumeration consists of elements ! 280: .i A , ! 281: .i B , ! 282: and ! 283: .i C , ! 284: then the choices are accessible as ! 285: .i A_case , ! 286: .i B_case , ! 287: and ! 288: .i C_case . ! 289: The consistency requirement is that ! 290: .i "designator" ! 291: contain the enumeration value corresponding to ! 292: which choice currently occupies the union. ! 293: .sh 2 "Constants" ! 294: .pp ! 295: The Courier specification is silent on the precise representations of ! 296: numbers and strings. In this implementation, numbers may be represented ! 297: as an optional minus sign followed by a ! 298: sequence of decimal digits or a sequence of octal digits followed ! 299: by ``B''. A constant of type STRING ! 300: is represented by a sequence of characters enclosed in ! 301: double quotation marks; it has the same restrictions as a C literal string (no ! 302: embedded newlines, use ``\e'' as a prefix character, etc.) except that double ! 303: double quotes are permitted to indicate quotations inside the string. ! 304: .sp 1 ! 305: .nf ! 306: minInt: INTEGER = -2147483648; -- or 20000000000B -- ! 307: lastCard: CARDINAL = 177777B; -- or 65535 -- ! 308: quotedName: STRING = "my name is \"jqj""\en"; ! 309: .fi ! 310: .sp 1 ! 311: .pp ! 312: Although the Courier language allows constants of ! 313: any type to be declared, ! 314: it is difficult to define constants of constructed types ! 315: in C programs. ! 316: This implementation normally handles constants by creating ! 317: static variables initialized to the values of the (possibly structured) ! 318: constants. The technique is not in general possible for structured ! 319: constants of type CHOICE ! 320: since they are implemented as C ``unions'', so this implementation does not ! 321: support constants which contain a non-null CHOICE. As a special case, ! 322: constants whose selected variant is a null record are allowed, e.g. ! 323: .sp 1 ! 324: myImmediate: BulkData.Immediate = immediate []; ! 325: .sp 1 ! 326: .sh 2 "Procedures" ! 327: .pp ! 328: PROCEDURE constants correspond to C functions plus a numeric jump table ! 329: index used in serialization of procedure calls on the network. ! 330: Procedure declarations may include argument lists, result lists, and ! 331: error lists. The argument list of the corresponding C function ! 332: contains one parameter for each parameter specified in the declaration, ! 333: but is ! 334: expanded by the addition of 2 parameters (see below). The result list ! 335: specifies a C structure to be returned by the C function containing the ! 336: results; for a procedure, Foo, this result list is of type FooResults. ! 337: Thus, a procedure declared ! 338: .sp 1 ! 339: Example: PROCEDURE [ ] RETURNS [ a: Foo, b: Baz ] = 0; ! 340: .sp 1 ! 341: would be implemented by a C function with no arguments ! 342: returning a structure of type ExampleResults with definition ! 343: something like ! 344: .sp 1 ! 345: .nf ! 346: typedef struct { ! 347: Foo a; ! 348: Baz b; ! 349: } ExampleResults ! 350: .fi ! 351: .sp 1 ! 352: Note that Foo and Baz might be arbitrarily complex structures; ! 353: see ``Constructed Types'' above. ! 354: .pp ! 355: Importing a PROCEDURE from a DEPENDS UPON module imports only its jump ! 356: table index. ! 357: .sh 2 "ERROR constants" ! 358: .pp ! 359: An ERROR constant corresponds to a defined numeric value, and possibly ! 360: to a C structure describing its argument list. For example, ! 361: .sp 1 ! 362: OtherError: ERROR [ errorstring: STRING ] = 1; ! 363: .sp 1 ! 364: corresponds approximately to the C declarations: ! 365: .sp 1 ! 366: .nf ! 367: #define OtherError (1+ERROR_OFFSET) ! 368: typedef struct { ! 369: String errorstring; ! 370: } OtherErrorArgs; ! 371: .fi ! 372: .sp 1 ! 373: Note that ERROR values must be specified in the range 0 to 65535, but ! 374: that they produce a symbol which appears to the C programmer ! 375: with values of type int ! 376: in the range 65536 to 131171 (in some contexts, the user may wish to ! 377: treat C error values in the range 0 to 65535 as Unix error numbers). ! 378: The structure corresponding to the error ! 379: may be useful in creating and referencing error arguments ! 380: in C functions. ! 381: .sh 2 "Scope of Names" ! 382: .pp ! 383: Given a courier program beginning ! 384: .sp ! 385: Example : PROGRAM 537 VERSION 1 = ! 386: .sp ! 387: types and constants declared in this program belong to the module ``Example1''; ! 388: Their full names have prefixed to them the program name and version number. ! 389: Given the declaration of ``minInt'' above, the XNS Courier compiler actually ! 390: produces a header file (here ! 391: .i Example1.h ) ! 392: containing the C declaration: ! 393: .sp 1 ! 394: .nf ! 395: static Integer Example1_minInt = {-2147483648}; ! 396: .fi ! 397: .sp 1 ! 398: .pp ! 399: This prefixing implies that a C programmer may refer to several different ! 400: remote Courier programs without risk of name collision. ! 401: For programmer convenience a second header file (here ! 402: .i Example1_defs.h ) ! 403: is also created by the compiler containing macro definitions which widen ! 404: the scope to ``Example1'' and allow the programmer to refer to types and ! 405: constants without explicit qualification. The programmer may include ! 406: whichever header file he prefers. ! 407: .pp ! 408: Note that types and constants obtained from a DEPENDS UPON inclusion must ! 409: be referenced using fully qualified form. Note also that structure members, ! 410: procedure arguments, and enum tags are not currently qualified; beware ! 411: potential name conflicts! ! 412: .sh 1 "Running the Courier Compiler" ! 413: .pp ! 414: The specification file is expected to have the extension ! 415: .i ".cr" . ! 416: Consider the following skeletal Courier program definition: ! 417: .sp ! 418: Example : PROGRAM 537 VERSION 1 = BEGIN ... END. ! 419: .sp ! 420: The name of this Courier program is ``Example''; it is version 1. ! 421: By convention, this specification would stored in the file ! 422: .i "Example1.cr" . ! 423: The ``537'' in this example is illustrative only; ! 424: the program number is uniquely allocated from the range of LongCardinals ! 425: by Xerox. See Appendix B of the Courier standard for program number ! 426: assignment procedures. ! 427: .pp ! 428: The first step is to use the Courier compiler on the specification. ! 429: Assuming ! 430: .i "xnscourier" ! 431: is in your path (it is ! 432: .i "/usr/new/xnscourier" ! 433: on the Berkeley distribution), type ! 434: .sp 1 ! 435: xnscourier Example1.cr ! 436: .sp 1 ! 437: If there are no errors in compilation, ! 438: the following files will be produced: ! 439: .(b ! 440: .TS ! 441: c c ! 442: l l. ! 443: File Contents ! 444: ! 445: Example1_defs.h scope widening macros (includes Example1.h) ! 446: Example1.h definitions, typedefs, and constant declarations ! 447: Example1_support.c routines to map between C and Courier ! 448: Example1_server.c server main program and support routines ! 449: Example1_client.c client routines to support applications calling Example ! 450: .TE ! 451: .)b ! 452: .pp ! 453: The header file ! 454: .i "Example1_defs.h" ! 455: should be included via ! 456: .sp ! 457: #include ``Example1_defs.h'' ! 458: .sp ! 459: in all user-written parts of the Courier program ! 460: (i.e., the implementations of the client program and server procedures.) ! 461: .pp ! 462: The ! 463: .i "\fB-I\fIinclude-directory" ! 464: switch may be used on the command line of ! 465: .i xnscourier ! 466: to establish a search path for Courier specification files named in a ! 467: DEPENDS UPON clause. By default, the compiler looks first in the current ! 468: directory followed by the courier-description-file named in the ! 469: .i /etc/Courierservices ! 470: file. ! 471: .sh 2 "Calling Remote Procedures \- Client Implementation" ! 472: .pp ! 473: A remote procedure appears to the C programmer to look almost like ! 474: a local procedure (although it typically makes use of resources not ! 475: available on the local machine or else runs many times slower ! 476: than would a local procedure). A C client program (i.e. the caller ! 477: of a remote procedure) actually calls a stub function generated by ! 478: the courier compiler, which in turn executes the code necessary to ! 479: invoke the corresponding procedure on the remote machine. ! 480: .pp ! 481: Before calling the remote procedure it is necessary to establish a ! 482: Courier SPP connection (in the process binding a local socket to ! 483: a remote machine). This may be done by the calling the function ! 484: CourierOpen(), passing it as argument a structure identifying the remote ! 485: host. This structure ! 486: is of type ``ns_addr'' ! 487: as defined in the include file ! 488: .i "#include <netns/ns.h>" . ! 489: CourierOpen() returns an anonymous pointer ! 490: (of type CourierConnection*) to a structure containing ! 491: the socket to be used for this courier call, which should be passed ! 492: to each remote procedure to be executed on the particular remote host. ! 493: CourierOpen() returns NULL if there is a problem opening an SPP connection ! 494: to the host specified. ! 495: .pp ! 496: For low-level access to ns_addr structures, ! 497: the procedure getXNSaddr() is available, taking a string in the form ! 498: ``network#a1.a2.a3.a4.a5.a6#socket'' (where all numbers are hex) or ! 499: ``networkhigh-networklow#d1-d2-d3-d4#socket'' (where all numbers are ! 500: 3-digit decimal numbers) ! 501: and returning a pointer to the ! 502: corresponding ns_addr structure. This result may then be passed as the ! 503: argument to CourierOpen(). ! 504: More normally, the user will have a string in the form of an NS address, ! 505: e.g. ``jqj:computer\ science:cornell-univ''. This string can be ! 506: coerced into a Clearinghouse ObjectName, then used in a Clearinghouse-based ! 507: lookup to find the corresponding address: ! 508: .sp 1 ! 509: .nf ! 510: Clearinghouse2_ObjectName hobj, hdefault; ! 511: struct ns_addr * host; ! 512: char * string; ! 513: ! 514: CH_NameDefault(&hdefault); /* get defaults */ ! 515: hobj = CH_StringToName(string, &hdefault); ! 516: host = CH_LookupAddr(hobj, 0); ! 517: .fi ! 518: .sp 1 ! 519: .pp ! 520: The SPP connection may be reused by multiple RPCs destined for the same ! 521: host, even if the procedures are part of different remote programs. However, ! 522: the SPP connection may not be reused while an RPC is outstanding; thus, ! 523: if you need to issue a Courier call during a BDT transfer, you will need ! 524: to obtain a second CourierConnection. ! 525: .pp ! 526: The client then calls the compiler-generated stub function to invoke ! 527: the remote procedure. This stub ! 528: function takes at least 2 arguments: ! 529: .ip (1) ! 530: The pointer returned from CourierOpen(). ! 531: If the SPP connection does not ! 532: currently exist (i.e. has been closed by the server) ! 533: it is reopened for this call. ! 534: .ip (2) ! 535: A pointer to a user-supplied function which will be used if a Bulk Data ! 536: Transfer is required (see the section on BDT below); if this remote ! 537: procedure does not involve Bulk Data Transfer, then the function pointer ! 538: should be 0 or NULL. ! 539: .ip (3...) ! 540: One additional argument corresponding to each parameter in the Courier ! 541: language description of this procedure. ! 542: .pp ! 543: The remote procedure returns in one of 2 ways: either it returns ! 544: normally, passing back a structure corresponding to the RETURNS parameters ! 545: specified in the procedure description, or it signals an error which ! 546: may be caught using the DURING ... HANDLER mechanism described in ! 547: .i except(1) . ! 548: .pp ! 549: A Courier procedure is allowed to return multiple results, a language feature ! 550: not easily mapped into C. To provide this functionality, all UNIX ! 551: courier remote procedures actually return a structure ! 552: containing the procedure results. Thus, for a procedure named ! 553: Foo, declared ! 554: .sp 1 ! 555: Foo: PROCEDURE [ ] RETURNS [str: STRING]; ! 556: .sp 1 ! 557: the C function Foo would return a structure of type FooResults, declared ! 558: .sp 1 ! 559: .nf ! 560: typedef struct { ! 561: String str; ! 562: } FooResults; ! 563: .fi ! 564: .fi ! 565: .pp ! 566: Instead of returning a value of type determined by the RESULTS clause ! 567: of a PROCEDURE declaration, a Courier remote procedure call may return ! 568: a reject message indicating the remote system's inability to even ! 569: attempt a remote operation, or an abort message raising a remote error, ! 570: i.e. reporting the operation's failure. Reject and abort messages are ! 571: handled by a signalling mechanism or if uncaught cause termination of ! 572: the client program. For example, a client calling the remote procedure ! 573: defined by ! 574: .sp 1 ! 575: .nf ! 576: NoSuchFile: ERROR = 0; ! 577: OtherError: ERROR [ errorstring: STRING ] = 1; ! 578: Example: PROCEDURE [ ] REPORTS [ NoSuchFile, OtherError ]; ! 579: .fi ! 580: .sp 1 ! 581: might be coded as ! 582: .sp 1 ! 583: .nf ! 584: conn = CourierOpen(destaddr); ! 585: DURING Example(conn,NULL) ! 586: HANDLER switch(Exception.Code) { ! 587: case REJECT_ERROR: ! 588: fprintf(stderr,"Remote reject.\en"); ! 589: exit(1); ! 590: case NoSuchFile: ! 591: fprintf(stderr,"No such file\en"); ! 592: break; ! 593: case OtherError: ! 594: fprintf(stderr,"Remote error %s\en", ! 595: CourierErrArgs(OtherErrorArgs,errorstring) ); ! 596: break; ! 597: case PROTOCOL_VIOLATION: ! 598: case INTERNAL_ERROR: ! 599: fprintf(stderr,"Local internal error %s\en", ! 600: Exception.Message); ! 601: break; ! 602: default: ! 603: fprintf(stderr,"Unknown error, type %d\en", ! 604: Exception.Code); ! 605: } ! 606: END_HANDLER ! 607: .fi ! 608: .sp 1 ! 609: The error value, Exception.code, will be one of (1) ``REJECT_ERROR'', ! 610: which indicates a REJECT message from the remote server, (2) a program-defined ! 611: ERROR value (offset by ERROR_OFFSET), (3) or ``INTERNAL_ERROR'' ! 612: or ``PROTOCOL_VIOLATION'' ! 613: indicating ! 614: a serious internal error in the Unix courier implementation; in the third ! 615: case, a string is available as Exception.Message. ! 616: Both errors and rejections may have arguments, which can be accessed ! 617: within a handler as a struct pointed to by Exception.Message. For ! 618: programmer convenience a macro, CourierErrArgs, is defined, taking ! 619: as arguments the typedef defining the current error's arguments ! 620: and the name of the argument to be accessed; this macro may be used ! 621: only within an error handler. ! 622: For rejection messages, the type should be ``rejectionDetails,'' ! 623: as defined on page 25 of the Courier spec. ! 624: .pp ! 625: When done with a particular remote connection, the client should call ! 626: CourierClose() with argument the pointer returned from CourierOpen() ! 627: to free the socket and cleanly close the connection. ! 628: .pp ! 629: The client program should be loaded with ! 630: .i "Example1_client.o" , ! 631: .i "Example1_support.o" , ! 632: and -lcourier (the XNS Courier library) ! 633: to produce an executable program. ! 634: .sh 2 "Writing a Courier Server" ! 635: .pp ! 636: The Courier protocol specifies a standard for communicating ! 637: parameters and results which has been adhered to in this ! 638: implementation. ! 639: It also specifies an initial connection protocol ! 640: and a format for messages. ! 641: .pp ! 642: There is a single Courier Daemon per UNIX machine whose function ! 643: is to listen on a well-known XNS port for Courier interface activation ! 644: requests. ! 645: A request contains the number of the Courier program ! 646: whose server is to be activated, and its version number. These two ! 647: numbers are used as a key in the file ! 648: .i /etc/Courierservices ! 649: to identify ! 650: the executable file associated with the particular courier program. ! 651: .i /etc/Courierservices ! 652: consists of a sequence of lines of the form ! 653: .sp 1 ! 654: program-name program-number version courier-description-file server-binary-file ! 655: .sp 1 ! 656: The daemon ! 657: either spawns the executable file ! 658: or replies with a reject message. Note that each incoming remote procedure ! 659: call may be serviced by a separate UNIX process. ! 660: This implies that connection-oriented services (services which consist of a ! 661: sequence of related procedure calls) must be implemented by maintaining ! 662: the state of connections in a file. ! 663: .pp ! 664: The executable file containing the server for a particular remote ! 665: program consists of a main program generated by the courier compiler ! 666: which interprets CALL messages from the remote client and calls ! 667: one or more user-written functions to implement the ! 668: courier procedures. ! 669: .pp ! 670: Given a courier specification for the remote program Example containing ! 671: remote procedure Example, ! 672: the server functions (including the C function Example and any support ! 673: routines needed) ! 674: should be loaded with ! 675: .i "Example1_server.o" , ! 676: .i "Example1_support.o" , ! 677: and -lcourier ! 678: to produce the server process that will be invoked whenever ! 679: an activation request arrives. ! 680: .pp ! 681: A server function receives an argument list similar to that passed by ! 682: a client to a remote procedure. The first two arguments in the ! 683: parameter list should be ignored; each succeeding argument corresponds ! 684: to one parameter in the courier declaration. ! 685: .pp ! 686: To return from a server function you must return a structure containing ! 687: the results of the procedure. If this structure contains pointers (e.g. ! 688: Strings) you should insure that the data pointed to is allocated ! 689: staticly or on the heap rather than on the stack. ! 690: You must not call exit(3) from within the server function; doing so will ! 691: cause the client (remote caller) to hang indefinitely waiting for a ! 692: reply. ! 693: .pp ! 694: To abort a server function, returning an error code, use the ! 695: ``raise(code, msg)'' ! 696: library function, with arguments the error code and either NULL (if the ! 697: error takes no arguments) or a pointer to a structure containing the ! 698: arguments to the error. Raise causes a longjump out of the function ! 699: directly to the handler, and thence to the remote procedure caller. ! 700: For example, to return the OtherError message defined above one might code ! 701: .sp 1 ! 702: .nf ! 703: OtherErrorArg randomerr; ! 704: . . . ! 705: randomerr.errorstring = sys_errlist[errno]); ! 706: raise(OtherError,(char*) &randomerr); ! 707: .fi ! 708: .sp 1 ! 709: .pp ! 710: When a server function returns to its main program caller the main() ! 711: sends the appropriate RETURN or ABORT message to the remote client, ! 712: and does an exit(0). The server then holds the connection for up to 90 ! 713: seconds waiting for another Courier call to arrive on the same SPP ! 714: stream. ! 715: .sh 2 "Dynamic Allocation" ! 716: .pp ! 717: One additional ! 718: .i caveat ! 719: is necessary when using Courier remote procedure calls from C: C does not ! 720: have garbage collection, so you should free any dynamically allocated storage ! 721: when you are done with it. In general, top level Courier objects are ! 722: always allocated from the stack or statically; however, strings and sequences ! 723: within an object are generally allocated from the heap using malloc(). ! 724: .pp ! 725: A client program which calls the remote procedure Foo defined above, ! 726: returning a STRING, ! 727: actually has returned to it a C struct containing a pointer to an array ! 728: of malloced characters. When done with this string, the program may free ! 729: it by calling clear_FooResults() with argument the address of the ! 730: result returned by the call to Foo. Thus: ! 731: .sp 1 ! 732: .nf ! 733: FooResults result; ! 734: . . . ! 735: result = Foo(conn,NULL); ! 736: . . . ! 737: printf("result was %s\n",result.str); ! 738: clear_FooResults(&result); ! 739: .fi ! 740: .sp 1 ! 741: .sh 1 "Using Bulk Data Transfer" ! 742: .pp ! 743: .sh 2 "Overview" ! 744: .pp ! 745: When a Courier program needs to transfer an arbitrary amount of ! 746: information as an argument or result of a Courier procedure, the ! 747: procedure is usually defined to have an argument of type ``BulkData.Sink'' ! 748: or ``BulkData.Source'' (and a ``DEPENDS UPON BulkData'' ! 749: is included in the program). ! 750: The argument is a ``source'' if it is information transferred from caller ! 751: to server (as though a procedure argument), a ``sink'' if it is ! 752: information transferred from server to caller (as though a procedure ! 753: result). In this ! 754: implementation, a Courier procedure may have at most one such argument, ! 755: which must have the value null or immediate. ! 756: In a Courier call, the bulk data is transmitted in a special way, ! 757: multiplexed into the same data path as control messages ! 758: between the arguments and the results. ! 759: .pp ! 760: The BDT user should include in the ! 761: .i ".cr" ! 762: file a ``DEPENDS UPON BulkData (0) VERSION 1'', then reference these ! 763: arguments as BulkData.Source and BulkData.Sink in PROCEDURE ! 764: declarations. ! 765: The declaration of a Courier PROCEDURE can then indicate a bulk ! 766: data transfer by including in its formal argument list a variable ! 767: of type BulkData.Source or BulkData.Sink ! 768: as appropriate. ! 769: The corresponding parameter to the C procedure will be of type ! 770: BulkData1_Descriptor. ! 771: .sh 2 "Coding a Client" ! 772: .pp ! 773: To use Bulk Data in a client program, the Courier definition of the ! 774: procedure must include one BulkData.Source or BulkData.Sink parameter. ! 775: As the actual argument to the call, the C client passes a constant, ! 776: either BulkData1_immediateSource, BulkData1_immediateSink, BulkData1_nullSource, ! 777: or BulkData1_nullSink as appropriate. The client ! 778: also specifies a pointer to a user-supplied function as the second ! 779: argument to the remote procedure. ! 780: Courier sets up the transaction, then calls the supplied function with ! 781: one argument, the pointer returned by CourierOpen() describing the ! 782: SPP socket on which to write (if a source argument) or read ! 783: (if a sink) the bulk data. ! 784: .pp ! 785: To complete the transaction normally, the ! 786: function should send packets of SPP data terminated by ! 787: an end-of-message (if writing) or read up to but not beyond an ! 788: end-of-message (if reading), and should return normally to its caller. ! 789: To do so, it should call the procedures BDTread(), BDTwrite(), or ! 790: BDTclosewrite() ! 791: as appropriate. BDTread() and BDTwrite() take arguments analogous to read() ! 792: and write() except that instead of a file descriptor they accept the SPP ! 793: descriptor returned by CourierOpen(); their arguments are thus ! 794: (1) the pointer to the SPP data structure, ! 795: (2) a pointer to an array of characters (the IO buffer), ! 796: and (3) a count; ! 797: they return the number of characters actually read or written. ! 798: .pp ! 799: The UNIX program can finish a write transfer by calling BDTclosewrite() or ! 800: BDTabort(), each with the SPP descriptor as argument. ! 801: BDTclosewrite() produces an SPP end-of-message, while BDTabort() instructs ! 802: Courier to discard any buffered data and ! 803: send a Bulk Data Abort to abort the transaction. A read transfer may also be ! 804: prematurely ended by calling BDTabort(). ! 805: .sh 2 "Coding a Server" ! 806: .pp ! 807: Writing a server that uses BDT ! 808: is similar to writing the BDT function in a caller. ! 809: The server is passed a BulkData.Source or BulkData.Sink (the two are ! 810: indistinguishable at run time). ! 811: The server routine should check to make sure that the source or sink ! 812: is of type null or immediate (active and passive descriptors are not ! 813: supported in this implementation) by means of code such as: ! 814: .sp 1 ! 815: .nf ! 816: Foo(bdtconnection,ignoredarg,..., s, ...) ! 817: CourierConnection *bdtconnection; ! 818: BulkData1_Sink s; ! 819: . . . ! 820: switch (s.designator) { ! 821: case active: ! 822: case passive: ! 823: /* can't raise BulkData1_InvalidDescriptor, so */ ! 824: raise(someerror); ! 825: case null: ! 826: /* handle null transfer */ ! 827: break; ! 828: case immediate: ! 829: /* handle normal, i.e. immediate, transfer */ ! 830: break; ! 831: } ! 832: .fi ! 833: .sp 1 ! 834: Note that the BDT error InvalidDescriptor cannot be used unless the ! 835: procedure declaration explicitly notes that this is a valid REPORTS error ! 836: type. Normally, each courier program defines its own error to be reported ! 837: on BDT problems. ! 838: .pp ! 839: Use the first argument to the server procedure as the handle on the ! 840: BDT connection. ! 841: As with a client BDTabort() may ! 842: be used to send abort messages. ! 843: .sh 2 "Sending Bulk Data" ! 844: .pp ! 845: Sending bulk data, either as client or server, is quite ! 846: straightforward. Use the supplied procedure BDTwrite(), whose ! 847: semantics are similar to write(). BDTwrite() will return -1 ! 848: if an error occurs or an abort from the listener is received; in this ! 849: case, the sender should immediately cease transmitting BDT data, and ! 850: should instead call BDTabort(). ! 851: Typical code to send data on ``bdtconnection'', ! 852: in this case read from a buffered file open ! 853: as ``source,'' might appear as: ! 854: .sp 1 ! 855: .nf ! 856: CourierConnection *bdtconnection; ! 857: FILE *source; ! 858: int count; ! 859: char buffer[SPPMAXDATA]; ! 860: ... ! 861: while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 && ! 862: BDTwrite(bdtconnection,buffer,count) >= 0 ) ! 863: ; ! 864: if (count <= 0) /* succsfull transfer */ ! 865: BDTclosewrite(bdtconnection); ! 866: else ! 867: BDTabort(bdtconnection); ! 868: .fi ! 869: .sp 1 ! 870: .pp ! 871: Each BDTwrite() transmits one packet after checking for an abort message ! 872: from the receiver. ! 873: A sender which gets an abort from a remote receiver must immediately ! 874: stop transferring data and instead must echo an abort via BDTabort() ! 875: to acknowledge the end of the transfer. ! 876: .pp ! 877: Also, for efficiency's ! 878: sake it is desirable to write packets as near as possible to the ! 879: nominal maximum length of an SPP packet; this suggests a buffer length of ! 880: 534 bytes. For programmer convenience, the file ! 881: .i courier.h ! 882: defines the symbol SPPMAXDATA as 534. ! 883: .sh 2 "Receiving Bulk Data" ! 884: .pp ! 885: To make receiving bulk data simple, the routine BDTread() performs checks for ! 886: various conditions on receipt of each packet. BDTread() will return the ! 887: count of the number of bytes actually read in each packet (skipping empty ! 888: packets), or 0 to indicate end of data. If an error or BDT abort message ! 889: occurs, BDTread() will return -1; if this occurs, the receiver should ! 890: immediately stop reading BDT data. ! 891: .pp ! 892: Typical code for receiving bulk data on the socket described by ! 893: ``bdtconnection'' and ! 894: depositing it in the file opened for buffered ouput as ``destfile'' ! 895: might be: ! 896: .sp 1 ! 897: .nf ! 898: CourierConnection *bdtconnection; /* BDT source descriptor */ ! 899: FILE *destfile; /* output file */ ! 900: char sppdata[SPPMAXDATA]; ! 901: ... ! 902: count = BDTread(bdtconnection, sppdata, SPPMAXDATA); ! 903: while (count > 0) { /* actually read data */ ! 904: if (fwrite(sppdata, 1, count, destfile) == 0) { ! 905: /* error while writing */ ! 906: BDTabort(bdtconnection); ! 907: break; ! 908: } ! 909: count = BDTread(bdtconnection, sppdata, SPPMAXDATA); ! 910: } ! 911: if (count == 0) { ! 912: /* successful */ ! 913: ... ! 914: .fi ! 915: .sp 1 ! 916: .sh 2 "StreamOf declarations" ! 917: .pp ! 918: Frequently, data to be sent on a Bulk Data connection will be described ! 919: by a StreamOfSomething declaration in the courier specification. For ! 920: example, in Clearinghouse several routines are documented as returning ! 921: a StreamOfObjectName; unfortunately, a StreamOf declaration is recursive, ! 922: and so is not immediately translatable into automatic packing and unpacking ! 923: routines which need to know in advance how much space to allocate for the ! 924: result. As a kludge, we translate such types as if the recursive ! 925: part of the declaration were a null record. ! 926: Consider: ! 927: .sp 1 ! 928: .nf ! 929: StreamOfObjectName: TYPE = CHOICE OF { ! 930: nextSegment (0) => RECORD [ ! 931: segment: SEQUENCE OF ObjectName, ! 932: restOfStream: StreamOfObjectName ], ! 933: lastSegment (1) => SEQUENCE OF ObjectName}; ! 934: .fi ! 935: .sp 1 ! 936: .lp ! 937: In the above declaration, the ``restOfStream is effectively ignored. ! 938: Given this declaration, the following routine may be passed to a ! 939: remote procedure as the Bulk Data actor. ! 940: .sp 1 ! 941: .nf ! 942: #include "Clearinghouse_support.c" /* get internalize_* routines */ ! 943: #define MAXPACKS 5 ! 944: ! 945: GetData(conn) ! 946: CourierConnection *conn; ! 947: { ! 948: int count, i; ! 949: Unspecified buffer[MAXWORDS*MAXPACKS], *bp, *bufend; ! 950: StreamOfObjectName obnames; ! 951: ! 952: bufend = buffer; ! 953: bp = buffer+MAXWORDS*(MAXPACKS-1); /* end of available space */ ! 954: while (count = BDTread(conn, (char*)bufend, ! 955: MAXWORDS*sizeof(Unspecified)) ! 956: ) { ! 957: bufend += count/sizeof(Unspecified); ! 958: if (bufend > bp) { ! 959: fprintf(stderr,"BDT read too big to fit\en"); ! 960: BDTabort(conn); ! 961: /* should clear out stuff here if we knew how much */ ! 962: } ! 963: } ! 964: bp = buffer; ! 965: while (bp < bufend) { ! 966: bp += internalize_StreamOfObjectName(&obnames,bp); ! 967: if (0 == (int) obnames.designator) ! 968: for (i = 0; i < obnames.nextSegment_case.segment.length; i++) ! 969: ProcessObjectName( ! 970: obnames.nextSegment_case.segment.sequence[i]); ! 971: else { ! 972: for (i = 0; i < obnames.lastSegment_case.length; i++) ! 973: ProcessObjectName( ! 974: obnames.lastSegment_case.sequence[i]); ! 975: return; ! 976: } ! 977: } ! 978: } ! 979: .fi ! 980: .sp 1 ! 981: .lp ! 982: Note that this code is very awkward, and that it requires that the whole ! 983: bulk data transfer be stored in memory before it is unpacked. Stay ! 984: tuned; we intend to modify the compiler to make the handling of such ! 985: streams much easier, at the cost of incompatibility with the existing ! 986: scheme, of course! ! 987: .sh 2 "Encapsulated Protocols" ! 988: .pp ! 989: Some courier programs use what might be termed ``encapsulated protocols'' ! 990: as a method of type punning to escape from the restrictions of the Courier ! 991: language. For example, in Filing ! 992: .sp 1 ! 993: .nf ! 994: Attribute: RECORD [ ! 995: type: AttributeType, value: SEQUENCE OF UNSPECIFIED ]; ! 996: checksum: AttributeType = 0; ! 997: Checksum: TYPE = CARDINAL; ! 998: createdBy: AttributeType = 1; ! 999: CreatedBy: TYPE = User; ! 1000: .fi ! 1001: .sp 1 ! 1002: Depending on the value of the type subfield, the data in the value field ! 1003: is intended to be interpreted as either a CARDINAL or a User name. ! 1004: .pp ! 1005: Such encapsulation is in general bad design unless a very compelling ! 1006: need for it exists. It is also awkward to support in a C program. The ! 1007: best way to handle such types is to write functions to code and decode ! 1008: the encapsulated value using the low level packing and unpacking routines ! 1009: provided by the Courier compiler. For example: ! 1010: .sp 1 ! 1011: .nf ! 1012: Filing4_User ! 1013: AttrToUser(attr) ! 1014: Filing4_Attribute *attr; ! 1015: { ! 1016: Unspecified buf[2049], *bp; /* space for biggest attribute */ ! 1017: Cardinal len; ! 1018: Filing4_User retval; ! 1019: ! 1020: /* useful fact: Item: TYPE = SEQUENCE OF UNSPECIFIED; */ ! 1021: externalize_Clearinghouse2_Item(&(attr->value), buf); ! 1022: bp = buf; ! 1023: bp += internalize_Cardinal(&len, bp); ! 1024: bp += internalize_Filing4_User(&retval, bp); ! 1025: return(retval); ! 1026: } ! 1027: ! 1028: UserToAttr(id, attr) ! 1029: Filing4_User id; ! 1030: Filing4_Attribute *attr; ! 1031: { ! 1032: Unspecified buf[2049], *bp; ! 1033: Cardinal len; ! 1034: ! 1035: /* don't know length yet, so leave space for it */ ! 1036: bp = buf + sizeof_Cardinal(len); ! 1037: len = externalize_Filing4_User(&id, bp); ! 1038: (void) externalize_Cardinal(&len, buf); ! 1039: internalize_Clearinghouse2_Item(&(attr->value), buf); ! 1040: return; ! 1041: } ! 1042: ! 1043: .fi ! 1044: .sp 1 ! 1045: Since the SEQUENCE OF UNSPECIFIED may have undergone arbitrary ! 1046: transformations ! 1047: during deserialization, translating it to a User record consists of first ! 1048: reserializing it into an array of char, then deserializing it using the ! 1049: low-level routine appropriate to the encapsulated type. Translating to ! 1050: an encapsulated type is an almost precise inverse. ! 1051: .sh 1 "Example I: Passwd.cr" ! 1052: .pp ! 1053: This section contains a Courier program which implements remote lookup in ! 1054: .i "/etc/passwd" ! 1055: (the UNIX database of user names, passwords, home directories, and so ! 1056: on). ! 1057: It does not utilize Bulk Data Transfer, but does illustrate most other ! 1058: features of this courier implementation. ! 1059: The applications programmer would first write ! 1060: .i PasswordLookup.cr , ! 1061: the description of the courier interface for the service, then might ! 1062: write ! 1063: .i PasswordLookup.c , ! 1064: containing the routines needed to implement a server for this courier ! 1065: program, or might write ! 1066: .i lookup.c , ! 1067: a typical client of this service. ! 1068: .bp ! 1069: .sh 2 "The specification (PasswordLookup.cr)" ! 1070: .sp 1 ! 1071: .nf ! 1072: PasswordLookup : PROGRAM 754 VERSION 1 = ! 1073: ! 1074: BEGIN ! 1075: ! 1076: -- This is a translation of the passwd structure in <pwd.h> ! 1077: ! 1078: Passwd : TYPE = RECORD [ ! 1079: pw_name, pw_passwd : STRING, ! 1080: pw_uid, pw_gid, pw_quota : LONG CARDINAL, ! 1081: pw_comment, pw_gecos, pw_dir, pw_shell : STRING ! 1082: ]; ! 1083: ! 1084: -- Remote Errors ! 1085: ! 1086: NoSuchUser : ERROR = 0; ! 1087: OtherError : ERROR [ errorstring: STRING ] = 1; ! 1088: ! 1089: -- Remote entry points. ! 1090: ! 1091: LookupUid : PROCEDURE [ uid : CARDINAL ] RETURNS [ passwd : Passwd ] ! 1092: REPORTS [ NoSuchUser ] ! 1093: = 0; ! 1094: ! 1095: LookupUser : PROCEDURE [ user : STRING ] ! 1096: RETURNS [ passwd : Passwd, forward : STRING ] ! 1097: REPORTS [ NoSuchUser, OtherError ] ! 1098: = 1; ! 1099: ! 1100: END. ! 1101: .fi ! 1102: .sp 2 ! 1103: .sh 2 "PasswordLookup_defs.h" ! 1104: .sp 1 ! 1105: .nf ! 1106: /* ! 1107: * Declarations for Courier program PasswordLookup. ! 1108: */ ! 1109: #include <courier.h> ! 1110: #include <except.h> ! 1111: ! 1112: typedef struct { ! 1113: String pw_name; ! 1114: String pw_passwd; ! 1115: LongCardinal pw_uid; ! 1116: LongCardinal pw_gid; ! 1117: LongCardinal pw_quota; ! 1118: String pw_comment; ! 1119: String pw_gecos; ! 1120: String pw_dir; ! 1121: String pw_shell; ! 1122: } Passwd; ! 1123: ! 1124: #define NoSuchUser 0 ! 1125: ! 1126: #define OtherError 1 ! 1127: typedef struct { ! 1128: String errorstring; ! 1129: } OtherErrorArg; ! 1130: ! 1131: typedef struct { ! 1132: Passwd passwd; ! 1133: } LookupUidResult; ! 1134: ! 1135: typedef struct { ! 1136: Passwd passwd; ! 1137: String forward; ! 1138: } LookupUserResult; ! 1139: ! 1140: extern LookupUidResult LookupUid(); ! 1141: ! 1142: extern LookupUserResult LookupUser(); ! 1143: .fi ! 1144: .sp 2 ! 1145: .sh 2 "Makefile" ! 1146: .sp 1 ! 1147: .nf ! 1148: CFLAGS = -O ! 1149: USEROBJS = lookup.o PasswordLookup_client.o ! 1150: SRVROBJS = PasswordLookup.o PasswordLookup_server.o ! 1151: LIBS = -lcr ! 1152: DESTDIR = /usr/lib/courier ! 1153: ! 1154: all: lookup PasswordLookup ! 1155: ! 1156: lookup: $(USEROBJS) ! 1157: cc -o lookup $(USEROBJS) $(LIBS) ! 1158: ! 1159: PasswordLookup: $(SRVROBJS) ! 1160: cc -o PasswordLookup $(SRVROBJS) $(LIBS) ! 1161: ! 1162: $(USEROBJS) $(SRVROBJS): PasswordLookup_defs.h ! 1163: ! 1164: PasswordLookup_defs.h \e ! 1165: PasswordLookup_server.c \e ! 1166: PasswordLookup_client.c: PasswordLookup.cr ! 1167: courier PasswordLookup.cr ! 1168: ! 1169: install: all ! 1170: install -s PasswordLookup $(DESTDIR) ! 1171: ! 1172: clean: ! 1173: -rm -f *.o PasswordLookup_*.c PasswordLookup_defs.h ! 1174: .fi ! 1175: .bp ! 1176: .sh 2 "The server procedures (PasswordLookup.c)" ! 1177: .sp 1 ! 1178: .nf ! 1179: #include <stdio.h> ! 1180: #include "PasswordLookup_defs.h" ! 1181: ! 1182: extern Passwd *getpwnam(), *getpwuid(); ! 1183: ! 1184: LookupUidResult ! 1185: LookupUid(CourierConnection,CourierBDTProc,uid) ! 1186: CourierConnection *CourierConnection, CourierBDTProc; ! 1187: Cardinal uid; ! 1188: { ! 1189: Passwd *pw; ! 1190: ! 1191: pw = getpwuid(uid); ! 1192: if (pw == NULL) ! 1193: raise(NoSuchUser,NULL); ! 1194: else { ! 1195: return (*(LookupUidResult*) &pw); ! 1196: } ! 1197: } ! 1198: ! 1199: LookupUserResult ! 1200: LookupUser(CourierConnection,CourierBDTProc,user) ! 1201: CourierConnection *CourierConnection, CourierBDTProc; ! 1202: String user; ! 1203: { ! 1204: LookupUserResult result; ! 1205: Passwd *pw; ! 1206: FILE *fwdfd; ! 1207: static char forward[100]; ! 1208: ! 1209: pw = getpwnam(user); ! 1210: if (pw == NULL) ! 1211: raise (NoSuchUser,NULL); ! 1212: else { ! 1213: sprintf(forward,"%s/.forward",pw->pw_dir); ! 1214: if ((fwdfd = fopen(forward,"r")) == NULL) { ! 1215: forward[0] = '\e0'; ! 1216: else { ! 1217: fgets(forward,100,fwdfd); ! 1218: fclose(fwdfd); ! 1219: if (strlen(forward) < 2) ( ! 1220: static OtherErrorArg randomerr = { ! 1221: "invalid forwarding file"}; ! 1222: raise(OtherError,&randomerr)); ! 1223: } ! 1224: result.password = *pw; ! 1225: result.forward = forward; ! 1226: return (result); ! 1227: } ! 1228: } ! 1229: .fi ! 1230: .bp ! 1231: .sh 2 "The user program (lookup.c)" ! 1232: .sp 1 ! 1233: .nf ! 1234: /* ! 1235: * Sample program to access remote password lookup. ! 1236: * Usage: lookup machine username ! 1237: */ ! 1238: #include <stdio.h> ! 1239: #include "PasswordLookup_defs.h" ! 1240: ! 1241: main(argc, argv) ! 1242: int argc; char **argv; ! 1243: { ! 1244: LookupUserResult result; ! 1245: Passwd passwd; ! 1246: CourierConnection *connection; ! 1247: ! 1248: if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) { ! 1249: fprintf(stderr,"Usage: %s machine file ...\en",argv[0]); ! 1250: exit(1); ! 1251: } ! 1252: if ((connection = CourierOpen(destaddr)) == NULL) { ! 1253: fprintf(stderr,"Can't open connection to %s\en",argv[1]); ! 1254: exit(1); ! 1255: } ! 1256: DURING ! 1257: result = LookupUser(connection,NULL,argv[2]); ! 1258: HANDLER ! 1259: switch(Exception.Code) { ! 1260: case Courier_reject: ! 1261: fprintf("Connection rejected, code = %d\en", ! 1262: CourierErrArgs(rejectionDetails,designator) ); ! 1263: exit(1); ! 1264: case NoSuchUser: ! 1265: printf("User %s unknown on %s.\en", argv[2], argv[1]); ! 1266: exit(0); ! 1267: case OtherError: ! 1268: fprintf(stderr,"Remote error %s\en", ! 1269: CourierErrArgs(OtherErrorArg,errorstring) ); ! 1270: exit(1); ! 1271: } ! 1272: END_HANDLER; ! 1273: ! 1274: displaypwd(& result.passwd); ! 1275: displayfwd(result.forward); ! 1276: CourierClose(connection); ! 1277: } ! 1278: ! 1279: displaypwd(p) ! 1280: Passwd *p; ! 1281: { ! 1282: printf("%s:%s:%d:%d:%s:%s:%s\en", ! 1283: p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, ! 1284: p->pw_gecos, p->pw_dir, p->pw_shell); ! 1285: } ! 1286: ! 1287: displayfwd(s) ! 1288: String s; ! 1289: { ! 1290: if (*s) printf("Mail forwarding to %s\en",s); ! 1291: else printf("Mail is not forwarded\en"); ! 1292: } ! 1293: .fi ! 1294: .bp ! 1295: .sh 1 "Example II: PrintFile" ! 1296: .pp ! 1297: This example is a very simple application of Bulk Data Transfer. ! 1298: .sh 2 "PrintFile.cr" ! 1299: .sp 1 ! 1300: .nf ! 1301: PrintFile: PROGRAM 756 VERSION 1 = ! 1302: BEGIN ! 1303: ! 1304: DEPENDS UPON BulkData (0) VERSION 1; ! 1305: ! 1306: -- Remote errors. ! 1307: ! 1308: CantPrint: ERROR = 0; ! 1309: ! 1310: -- Remote entry points. ! 1311: ! 1312: RPrint: PROCEDURE [ s: BulkData.Source ] ! 1313: REPORTS [ CantPrint ]; ! 1314: ! 1315: END. ! 1316: .fi ! 1317: .sp 1 ! 1318: .sh 2 "A typical client (remoteprint.c)" ! 1319: .lp ! 1320: .sp 1 ! 1321: .nf ! 1322: /* ! 1323: * Sample proram to print a file remotely using trivial remote print ! 1324: * protocol. ! 1325: * Usage: remoteprint filename ! 1326: * ! 1327: */ ! 1328: #include <stdio.h> ! 1329: #include <sys/types.h> ! 1330: #include <netns/ns.h> /* for XNS addresses */ ! 1331: #include "PrintFile_defs.h" ! 1332: ! 1333: static FILE * source; /* communicate from main to SendSource */ ! 1334: ! 1335: main(argc, argv) ! 1336: int argc; ! 1337: char *argv[]; ! 1338: { ! 1339: CourierConnection *connection; ! 1340: struct ns_addr *destaddr; ! 1341: ! 1342: if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) { ! 1343: fprintf(stderr,"Usage: %s machine file ...\en",argv[0]); ! 1344: exit(1); ! 1345: } ! 1346: if ((connection = CourierOpen(destaddr)) == NULL) { ! 1347: fprintf(stderr,"Can't open connection to %s\en",argv[1]); ! 1348: exit(1); ! 1349: } ! 1350: argv++; ! 1351: while (argc-- > 2) { ! 1352: argv++; ! 1353: if (strcmp(argv[0],"-") == 0) source = stdin; ! 1354: else source = fopen(argv[0],"r"); ! 1355: if (source == NULL) ! 1356: fprintf(stderr,"Can't open %s\en",argv[0]); ! 1357: else DURING ! 1358: RPrint(connection,SendSource,immediateSource); ! 1359: HANDLER ! 1360: fprintf(stderr,"Call to RPrint failed.\en"); ! 1361: END_HANDLER; ! 1362: fclose(source); ! 1363: } ! 1364: CourierClose(connection); ! 1365: } ! 1366: ! 1367: SendSource(bdtconnection) ! 1368: CourierConnection *bdtconnection; ! 1369: { ! 1370: int count; ! 1371: char buffer[SPPMAXDATA]; ! 1372: ! 1373: while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 && ! 1374: BDTwrite(bdtconnection,buffer,count) >= 0 ) ! 1375: ; ! 1376: if (count <= 0) ! 1377: BDTclosewrite(bdtconnection); /* last packet with EOM set */ ! 1378: else ! 1379: BDTabort(bdtconnection); ! 1380: } ! 1381: .fi ! 1382: .sp 2 ! 1383: .sh 2 "Server (PrintFile.c)" ! 1384: .sp 1 ! 1385: .nf ! 1386: #include <stdio.h> ! 1387: #include <sys/types.h> ! 1388: #include <netxns/ns.h> ! 1389: #include "PrintFile_defs.h" ! 1390: ! 1391: RPrintResult ! 1392: Rprint(source,CourierBDTProc,s) ! 1393: CourierConnection *source, CourierBDTProc; ! 1394: BulkData1_Source s; ! 1395: { ! 1396: FILE *printpipe; ! 1397: char sppdata[SPPMAXDATA]; ! 1398: ! 1399: switch (s.designator) ! 1400: case active: ! 1401: case passive: ! 1402: raise(CantPrint); ! 1403: /*NOTREACHED*/ ! 1404: case null: ! 1405: system("print /dev/null"); /* print a null file */ ! 1406: return; ! 1407: case immediate: ! 1408: if ((printpipe = popen("print","w")) == NULL) { ! 1409: raise(CantPrint); ! 1410: /*NOTREACHED*/ ! 1411: } ! 1412: count = BDTread(source, sppdata, SPPMAXDATA); ! 1413: while (count > 0) { /* actually read data */ ! 1414: if (fwrite(sppdata, 1, count, printpipe) == 0) { ! 1415: BDTabort(source); ! 1416: break; ! 1417: } ! 1418: count = BDTread(source, sppdata, SPPMAXDATA); ! 1419: } ! 1420: if (pclose(printpipe) == 0 && count == 0) ! 1421: return; ! 1422: else raise(CantPrint); ! 1423: } ! 1424: .fi ! 1425: .sh 1 "One Final Example" ! 1426: .pp ! 1427: Finally, we present a slightly ! 1428: more useful example, a program to print an Interpress ! 1429: file on a Services-8 printer. It depends on the standard Xerox Printing ! 1430: specification, program 4 version 3. For an even better example, look ! 1431: at the printing client, xnsprint, provided with the XNS Courier distribution ! 1432: as ! 1433: .i "./examples/print/xnsprint.c" . ! 1434: .sp 2 ! 1435: .nf ! 1436: #include <stdio.h> ! 1437: #include <sys/types.h> ! 1438: #include <netns/ns.h> ! 1439: #include "Printing_defs.h" ! 1440: #include <except.h> ! 1441: ! 1442: static FILE *ipfile = NULL; ! 1443: ! 1444: SendSource(bdtconnection) ! 1445: CourierConnection *bdtconnection; ! 1446: { ! 1447: int count; ! 1448: char buffer[SPPMAXDATA]; ! 1449: ! 1450: while ( (count = fread(buffer,1,SPPMAXDATA,ipfile)) > 0 && ! 1451: BDTwrite(bdtconnection,buffer,count) >= 0 ) ! 1452: ; ! 1453: if (count <= 0) ! 1454: BDTclosewrite(bdtconnection); /* last packet with EOM set */ ! 1455: else ! 1456: BDTabort(bdtconnection); ! 1457: } ! 1458: ! 1459: main(argc, argv) ! 1460: int argc; ! 1461: char *argv[]; ! 1462: { ! 1463: PrintResults result; ! 1464: struct ns_addr *destaddr; ! 1465: CourierConnection *conn; ! 1466: extern struct ns_addr *getXNSaddr(); ! 1467: PrintAttributes attributes; ! 1468: PrintOptions options; ! 1469: ! 1470: /* use Cornell print server, CornellS1 (slander) */ ! 1471: destaddr = getXNSaddr("2-273#2-852-159-207"); ! 1472: attributes.length = 0; ! 1473: options.length = 0; ! 1474: if (argc != 2 || ((ipfile = fopen(argv[1],"r")) == NULL)) { ! 1475: fprintf(stderr,"Usage: %s file\en",argv[0]); ! 1476: exit(1); ! 1477: } ! 1478: if ((conn = CourierOpen(destaddr)) == NULL) { ! 1479: fprintf(stderr,"Can't open connection to %s\en",xnshost); ! 1480: exit(1); ! 1481: } ! 1482: ! 1483: DURING ! 1484: result = Print(conn, SendSource, BulkData1_immediateSource, ! 1485: attributes, options); ! 1486: HANDLER { ! 1487: switch (Exception.Code) { ! 1488: case Busy: ! 1489: fprintf(stderr,"Busy\en"); ! 1490: break; ! 1491: case ConnectionError: ! 1492: fprintf(stderr,"Connection error, %d\en", ! 1493: CourierErrArgs(ConnectionErrorArgs,problem)); ! 1494: break; ! 1495: case InsufficientSpoolSpace: ! 1496: case SpoolingQueueFull: ! 1497: fprintf(stderr,"Insufficient spool space\en"); ! 1498: break; ! 1499: case SpoolingDisabled: ! 1500: fprintf(stderr,"Spooling disabled\en"); ! 1501: break; ! 1502: case MasterTooLarge: ! 1503: case TooManyClients: ! 1504: case ServiceUnavailable: ! 1505: case SystemError: ! 1506: case InvalidPrintParameters: ! 1507: case MediumUnavailable: ! 1508: case TransferError: ! 1509: fprintf(stderr,"Some Printing error, number %d\en", ! 1510: Exception.code-ERROR_OFFSET); ! 1511: break; ! 1512: case Undefined: ! 1513: fprintf(stderr,"Undefined error, number %d\en", ! 1514: CourierErrArgs(UndefinedArgs,problem)); ! 1515: break; ! 1516: case REJECT_ERROR: ! 1517: fprintf(stderr,"REJECT: type = %d\en", ! 1518: CourierErrArgs(rejectionDetails, designator)); ! 1519: break; ! 1520: default: ! 1521: fprintf(stderr,"Some random error, code %d\en", ! 1522: Exception.Code); ! 1523: break; ! 1524: } ! 1525: exit(1); ! 1526: } END_HANDLER; ! 1527: ! 1528: CourierClose(conn); ! 1529: printf("Done. Request ID %x %x %x %x %x\en", ! 1530: result.printRequestID[0], result.printRequestID[1], ! 1531: result.printRequestID[2], result.printRequestID[3], ! 1532: result.printRequestID[4]); ! 1533: } ! 1534: .fi ! 1535: .bp ! 1536: .sh 1 "Final Notes" ! 1537: .pp ! 1538: The issues of authentication and protection are ! 1539: difficult. ! 1540: They are only touched upon in this implementation. ! 1541: Currently, each Courier program must perform any authentication ! 1542: (presumably using the Authentication protocol) ! 1543: if this is desired. A few routines exist to allow cleints and servers ! 1544: to perform simple Authentication; no support for strong Authentication ! 1545: has yet been written. ! 1546: .pp ! 1547: This implementation is fairly inefficient, especially in ! 1548: the implementation of servers. Courier calls require substantial ! 1549: extra copying of courier arguments and results. More significantly, ! 1550: the requirement that each call spawn a unique process is very expensive ! 1551: in UNIX, and should be reconsidered. ! 1552: A sequence of RPCs on the same SPP stream that specifies the same program ! 1553: and version number uses the same process, but in all other cases a new ! 1554: process must be spawned for each individual RPC. ! 1555: .pp ! 1556: This implementation defines a number of reserved identifiers. ! 1557: .b ">>>should list them here<<<" ! 1558: .sh 1 "Appendix" ! 1559: .pp ! 1560: This appendix contains the grammar for the Courier language. ! 1561: It is similar to the YACC specification used ! 1562: by the Courier compiler. ! 1563: .sp ! 1564: .ps -2p ! 1565: .vs -2p ! 1566: .nf ! 1567: .TS ! 1568: l l l l l. ! 1569: %token identifier number string ! 1570: ARRAY BEGIN BOOLEAN CARDINAL ! 1571: CHOICE DEPENDS END ERROR ! 1572: FALSE INTEGER LONG OF ! 1573: PROCEDURE PROGRAM RECORD REPORTS ! 1574: RETURNS SEQUENCE STRING TRUE ! 1575: TYPE UNSPECIFIED UPON VERSION ! 1576: .TE ! 1577: %% ! 1578: ! 1579: Program : ! 1580: identifier ':' PROGRAM number VERSION number '=' ! 1581: BEGIN DependencyList DeclarationList END '.' ! 1582: | ! 1583: identifier ':' PROGRAM '=' ! 1584: BEGIN DependencyList DeclarationList END '.' ! 1585: ; ! 1586: ! 1587: DependencyList : ! 1588: /* empty */ ! 1589: | DEPENDS UPON ReferencedProgramList ';' ! 1590: ; ! 1591: ! 1592: ReferencedProgramList : ! 1593: ReferencedProgram ! 1594: | ReferencedProgramList ',' ReferencedProgram ! 1595: ; ! 1596: ! 1597: ReferencedProgram : ! 1598: identifier '(' number ')' VERSION number ! 1599: ; ! 1600: ! 1601: DeclarationList : ! 1602: /* empty */ ! 1603: | DeclarationList Declaration ! 1604: ; ! 1605: ! 1606: Declaration : ! 1607: identifier ':' TYPE '=' Type ';' ! 1608: | identifier ':' Type '=' Constant ';' ! 1609: ; ! 1610: ! 1611: Type : ! 1612: PredefinedType ! 1613: | ConstructedType ! 1614: | ReferencedType ! 1615: ; ! 1616: ! 1617: PredefinedType : ! 1618: BOOLEAN ! 1619: | CARDINAL ! 1620: | LONG CARDINAL ! 1621: | INTEGER ! 1622: | LONG INTEGER ! 1623: | STRING ! 1624: | UNSPECIFIED ! 1625: | LONG UNSPECIFIED ! 1626: ; ! 1627: ! 1628: ConstructedType : ! 1629: '{' CorrespondenceList '}' ! 1630: | ARRAY NumericValue OF Type ! 1631: | SEQUENCE MaximumNumber OF Type ! 1632: | RECORD '[' FieldList ']' ! 1633: | RECORD '[' ']' ! 1634: | CHOICE DesignatorType OF '{' CandidateList '}' ! 1635: | PROCEDURE ArgumentList ResultList ErrorList ! 1636: | ERROR ArgumentList ! 1637: ; ! 1638: ! 1639: ReferencedType : ! 1640: identifier ! 1641: | identifier '.' identifier ! 1642: ; ! 1643: ! 1644: CorrespondenceList : ! 1645: Correspondence ! 1646: | CorrespondenceList ',' Correspondence ! 1647: ; ! 1648: ! 1649: Correspondence : ! 1650: identifier '(' NumericValue ')' ! 1651: ; ! 1652: ! 1653: MaximumNumber : ! 1654: NumericValue ! 1655: | /* empty */ ! 1656: ; ! 1657: ! 1658: NumericValue : ! 1659: number ! 1660: | ReferencedConstant ! 1661: ; ! 1662: ! 1663: DesignatorType : ! 1664: /* empty */ ! 1665: | ReferencedType ! 1666: ; ! 1667: ! 1668: CandidateList : ! 1669: Candidate ! 1670: | CandidateList ',' Candidate ! 1671: ; ! 1672: ! 1673: Candidate : ! 1674: DesignatorList '=''>' Type ! 1675: ; ! 1676: ! 1677: DesignatorList : ! 1678: Designator ! 1679: | DesignatorList ',' Designator ! 1680: ; ! 1681: ! 1682: Designator : ! 1683: identifier ! 1684: | Correspondence ! 1685: ; ! 1686: ! 1687: ArgumentList : ! 1688: /* empty */ ! 1689: | '[' FieldList ']' ! 1690: ; ! 1691: ! 1692: ResultList : ! 1693: /* empty */ ! 1694: | RETURNS '[' FieldList ']' ! 1695: ; ! 1696: ! 1697: ErrorList : ! 1698: /* empty */ ! 1699: | REPORTS '[' NameList ']' ! 1700: ; ! 1701: ! 1702: FieldList : ! 1703: Field ! 1704: | FieldList ',' Field ! 1705: ; ! 1706: ! 1707: Field : ! 1708: NameList ':' Type ! 1709: ; ! 1710: ! 1711: Constant : ! 1712: PredefinedConstant ! 1713: | ConstructedConstant ! 1714: | ReferencedConstant ! 1715: ; ! 1716: ! 1717: PredefinedConstant : ! 1718: TRUE ! 1719: | FALSE ! 1720: | number ! 1721: | '-' number ! 1722: | '"' string '"' ! 1723: ; ! 1724: ! 1725: ConstructedConstant : ! 1726: identifier ! 1727: | '[' ElementList ']' ! 1728: | '[' ComponentList ']' ! 1729: | '['']' ! 1730: | identifier Constant ! 1731: | number ! 1732: ; ! 1733: ! 1734: ReferencedConstant : ! 1735: identifier ! 1736: | identifier '.' identifier ! 1737: ; ! 1738: ! 1739: ElementList : ! 1740: Constant ! 1741: | ElementList ',' Constant ! 1742: ; ! 1743: ! 1744: ComponentList : ! 1745: Component ! 1746: | ComponentList ',' Component ! 1747: ; ! 1748: ! 1749: Component : ! 1750: NameList ':' Constant ! 1751: ; ! 1752: ! 1753: NameList : ! 1754: identifier ! 1755: | NameList ',' identifier ! 1756: ; ! 1757: .fi ! 1758: .vs ! 1759: .ps
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.