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