From a41ca08e48598a7302977fa20c01231af609370c Mon Sep 17 00:00:00 2001 From: CSRG Date: Mon, 16 Mar 1987 15:33:51 -0800 Subject: [PATCH] BSD 4_4_Lite2 development Work on file usr/src/contrib/xns/doc/courier.tbl.me Synthesized-from: CSRG/cd3/4.4BSD-Lite2 --- usr/src/contrib/xns/doc/courier.tbl.me | 1759 ++++++++++++++++++++++++ 1 file changed, 1759 insertions(+) create mode 100644 usr/src/contrib/xns/doc/courier.tbl.me diff --git a/usr/src/contrib/xns/doc/courier.tbl.me b/usr/src/contrib/xns/doc/courier.tbl.me new file mode 100644 index 0000000000..5bbcd9c1e1 --- /dev/null +++ b/usr/src/contrib/xns/doc/courier.tbl.me @@ -0,0 +1,1759 @@ +\" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $ +\" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $ +\" $Log: courier.tbl.me,v $ +\" Revision 1.8 87/03/17 09:32:52 ed +\" Added -I switch to establish search path for DEPENDS UPON files. +\" +\" Revision 1.7 86/11/22 07:12:05 jqj +\" small changes reflecting differences between 4.3bsd and 4.2 Maryland XNS. +\" +\" Revision 1.6 86/05/12 09:02:43 jqj +\" minor revisions +\" +\" Revision 1.5 85/03/26 06:30:58 jqj +\" Revised public alpha-test version, released 26 March 1985 +\" +\" Revision 1.4 85/03/11 16:46:01 jqj +\" Public alpha-test version, released 11 March 1985 +\" +.lg 0 +.fo ''%'' +.(l C +.ps +4 +.b +XNS Courier under UNIX (4.3BSD) +.ps -4 +.sp 2 +.r +.he $ XNS Courier $ $Date: 87/03/17 09:32:52 $ +J.Q. Johnson +Computer Science Department +Cornell University +405 Upson Hall +Ithaca, NY 14853 +.)l +.sh 1 "Introduction" +.pp +This document describes the implementation and use +of the Courier remote procedure call protocol for Berkeley UNIX +(version 4.3), with XNS. +.sp 1 +.nf +.b +.(c +******************************************************** +Warning: this document is a DRAFT design specification. +It has not yet been fully implemented, and the details of +the interface it specifies (particularly with respect to +Courier servers) are subject to change. Send design +comments to jqj@cornell.ARPA or jqj@cornell.UUCP. +******************************************************** +.)c +.r +.fi +.sp 1 +.uh "Changes since previous revision:" +.np +Added discussion of encapsulated protocols. +.np +Added discussion of Clearinghouse library calls for finding a connection, +and relationship of connections to outstanding RPCs. +.uh "Earlier changes:" +.np +Changed names of generated file names to include version number. +Added discussion on scope of names vs. DEPENDS UPON modules. +.np +Users must now link Courier program ``Foo'' with Foo1_support.o. +.np +Added some discussion of freeing dynamic storage via clear_Foo. +.np +Changed +.i CourierOpen() +to take ns_addr structure rather than a string host name. Routines for +converting from name to address (using Clearinghouse) have not yet been +designed. +.np +Changed ERROR discussion to indicate that an ERROR is mapped to a long +with value > 65535. +.np +Changed description of BDT to reflect use of +.i BDTread() +and +.i BDTwrite() . +.np +Added SPPMAXDATA. +.np +Changed discussion of BulkData constants to indicate that things now +work correctly. +.sh 2 "General Description" +.pp +Courier is both a protocol and a specification language. +The protocol describes how remote procedures are invoked and +how parameters and results of various data types are transmitted. +The specification language, somewhat reminiscent of Mesa, +provides a simple way of defining the remote interfaces of distributed +programs. +.pp +This implementation is an attempt to allow UNIX C programmers to +construct distributed Courier programs to communicate with other +Courier implementations layered on XNS. It thus requires kernel +support for XNS Sequenced Packet Protocol sockets. +.pp +The simplest form of distributed program using Courier +consists of three parts. +The first is the specification, +written in the Courier language. +The specification must declare all procedures of +the program that will be called remotely (the +.i "server procedures" ). +The next part is the implementation, in C, of the server procedures. +Finally, there is the client portion of the program, also in C, which calls +the server procedures. +Note that either the client or server portion of the program may be +omitted if the UNIX program is designed to act with a non-UNIX +partner; for example, a simple printing client under UNIX might send +files to a Xerox laser printer using the Printing protocol published +and supported by Xerox. +.sh 2 "Acknowledgements" +.pp +Contributors to the current implementation have included +JQ Johnson, the primary implementor of the current version of the +Courier compiler under UNIX, +Eric Cooper, the author of the original UNIX Courier/TCP compiler, +Jeff Mogul, the author of the exception signalling mechanism used +here to handle abort and reject messages, +James O'Toole, Chris Torek, and Mark Weiser, +the authors of the 4.2BSD version of UNIX kernel support for XNS, +Keith Sklower, the author of the present (4.3BSD) version of UNIX kernel support, +Lee Moore, and Bill Nesheim. +.pp +Some of the software is copyright 1984 by Eric Cooper, and is for +research use only; it may be freely distributed but may not be +sold as a commercial product. +.sh 2 "References" +.pp +This document assumes familiarity with the Courier language, +details of which may be found in +the Courier protocol definition, +.i "Courier: The Remote Procedure Call Protocol" , +Xerox System Integration Standard 038112, +December 1981, and ``Appendix F, Bulk Data Transfer,'' Xerox System +Integration Standard 038112 Add. 1, October 1982. +However, for reference purposes, a grammar for the Courier language +may be found in the appendix to this document. +.pp +Users of Bulk Data Transfer will need to interact with SPP +(Sequenced Packet Protocol) +streams, and may wish to see +.i "Internet Transport Protocols" , +Xerox System Integration Standard 028112, December 1981. +.pp +Those interested in the workings of UNIX networking might consult +``A 4.2bsd Interprocess Communications Primer,'' +(DRAFT of March 11, 1983), by Samuel J. Leffler, Robert S. Fabry, and +William N. Joy, and the +``4.2BSD System Manual,'' +(Revised July, 1983), by William Joy +.i "et al" . +.pp +This document is also supplemented by various UNIX manual pages, notably +.i "xnscourier(3) " , +and +.i "except(3)" . +.sh 2 "Software and Hardware Dependencies" +.pp +This package requires the following software to operate: +.(l +4.3BSD (currently tested only for VAX and Gould versions) +PCC, 4.3BSD version (probably\-limited testing has been done with Tartan C) +.)l +.pp +To communicate with Xerox products you will need an Ethernet +interface. Most of the existing example applications depend heavily on +the existence of a Xerox Clearinghouse somewhere on the local network. +.sh 1 "The Courier Specification Language" +.pp +The Courier specification language is documented (loosely) in the Xerox +``Courier: The Remote Procedure Call Protocol.'' Our compiler +implements a large subset of the specification. Note that the embedding +of Courier constructs in a message or bit stream is usually irrelevant to the +applications programmer, who need only be concerned with the semantics +of the Courier datatypes. The courier compiler and runtimes translate +Courier datatypes into more easily managed C constructs and handle the +protocol of communication with the remote system. +.pp +This implementation places several restrictions on the specifications +it will accept: +.np +No forward references are permitted in the courier +specification. The user must reorder type specifications so that all +types and constants are defined before they are used. +Special provisions are made for recursive type declarations of the form +typically found in Bulk Data ``StreamOf'' declarations; see the +discussion of BDT below for details. General recursive declarations, +e.g. that of the Filing ``Filter,'' are not supported. +.np +The Courier specification is unclear as to lexical issues, +name scope, and name overloading. +In the current compiler, names should contain only upper and lower case +letters or digits. Also, all constants, types, and +enumeration tags within a module should have distinct names. Enumeration +tag names are currently global; thus, different enumeration types, even +in different modules, may not have the same tags. +.np +Constants of type CHOICE are not fully supported. In particular, they +are allowed only if the active arm of the choice is a nill record. +.np +Constants of type SEQUENCE are not supported. Constants of type ARRAY +(which have the same syntax as SEQUENCE constants) are supported, but are +not rangechecked; ``Vect: ARRAY 10 OF INTEGER = {1,2,3}'' is accepted, +though illegal. +.np +This implementation supports DEPENDS UPON and +referenced types and constants. However, the semantics of referenced +enumeration constants is unclear. Also, this implementation +does not support recursive dependencies. +.lp +We think we've eliminated all other restrictions, but we're probably wrong. +.sh 2 "Predefined types" +.pp +The following typedefs correspond to predefined Courier types: +.(b +.TS +c c c +l l l. +Courier type C typedef Implemented (on VAX) as + +BOOLEAN Boolean char (0==TRUE, 1==FALSE) +CARDINAL Cardinal unsigned short +LONG CARDINAL LongCardinal unsigned long +INTEGER Integer short +LONG INTEGER LongInteger long +STRING String char* +UNSPECIFIED Unspecified unsigned short +LONG UNSPECIFIED LongUnspecified unsigned long +.TE +.)b +Note that the representations of these types as seen by the applications +programmer are quite different from those sent over a Courier SPP connection. +For example, on the VAX a Cardinal's bytes are swapped before being +sent out in network byte order. A UNIX String corresponds to a pointer to +a null-terminated array of characters while a Courier serialized STRING +contains a count directly followed by an array of characters. Note +in particular that this implementation does +not support Courier STRINGs containing the ascii character NUL. +.sh 2 "Constructed types" +.pp +The Courier enumeration, array, and (non-null) record types +correspond to C enumerations, arrays, and structures respectively. +The Courier null record corresponds to an int. +.pp +The Courier sequence and choice types +pose some problems when they are mapped into C. +This is because an object of one of these types +must contain run-time information (the length of the sequence +or which choice is present) +that is implicit in Courier, but must be made explicit +in C. +Furthermore, the C programmer must bear the responsibility of keeping +this information consistent. +.pp +A sequence type is mapped into a structure consisting +of a Cardinal called +.i "length" , +and a pointer to the sequence elements called +.i "sequence" . +The consistency requirement is that +.i "length" +indicate the number of elements in the array pointed to +by +.i "sequence" . +.pp +A choice type is mapped into a structure consisting +of an enumeration element called +.i "designator" , +and a union of all the possible choices. +The designator is of the enumeration type defined +(implicitly or explicitly) +in the declaration of the choice type. +If this enumeration consists of elements +.i A , +.i B , +and +.i C , +then the choices are accessible as +.i A_case , +.i B_case , +and +.i C_case . +The consistency requirement is that +.i "designator" +contain the enumeration value corresponding to +which choice currently occupies the union. +.sh 2 "Constants" +.pp +The Courier specification is silent on the precise representations of +numbers and strings. In this implementation, numbers may be represented +as an optional minus sign followed by a +sequence of decimal digits or a sequence of octal digits followed +by ``B''. A constant of type STRING +is represented by a sequence of characters enclosed in +double quotation marks; it has the same restrictions as a C literal string (no +embedded newlines, use ``\e'' as a prefix character, etc.) except that double +double quotes are permitted to indicate quotations inside the string. +.sp 1 +.nf +minInt: INTEGER = -2147483648; -- or 20000000000B -- +lastCard: CARDINAL = 177777B; -- or 65535 -- +quotedName: STRING = "my name is \"jqj""\en"; +.fi +.sp 1 +.pp +Although the Courier language allows constants of +any type to be declared, +it is difficult to define constants of constructed types +in C programs. +This implementation normally handles constants by creating +static variables initialized to the values of the (possibly structured) +constants. The technique is not in general possible for structured +constants of type CHOICE +since they are implemented as C ``unions'', so this implementation does not +support constants which contain a non-null CHOICE. As a special case, +constants whose selected variant is a null record are allowed, e.g. +.sp 1 + myImmediate: BulkData.Immediate = immediate []; +.sp 1 +.sh 2 "Procedures" +.pp +PROCEDURE constants correspond to C functions plus a numeric jump table +index used in serialization of procedure calls on the network. +Procedure declarations may include argument lists, result lists, and +error lists. The argument list of the corresponding C function +contains one parameter for each parameter specified in the declaration, +but is +expanded by the addition of 2 parameters (see below). The result list +specifies a C structure to be returned by the C function containing the +results; for a procedure, Foo, this result list is of type FooResults. +Thus, a procedure declared +.sp 1 + Example: PROCEDURE [ ] RETURNS [ a: Foo, b: Baz ] = 0; +.sp 1 +would be implemented by a C function with no arguments +returning a structure of type ExampleResults with definition +something like +.sp 1 +.nf + typedef struct { + Foo a; + Baz b; + } ExampleResults +.fi +.sp 1 +Note that Foo and Baz might be arbitrarily complex structures; +see ``Constructed Types'' above. +.pp +Importing a PROCEDURE from a DEPENDS UPON module imports only its jump +table index. +.sh 2 "ERROR constants" +.pp +An ERROR constant corresponds to a defined numeric value, and possibly +to a C structure describing its argument list. For example, +.sp 1 + OtherError: ERROR [ errorstring: STRING ] = 1; +.sp 1 +corresponds approximately to the C declarations: +.sp 1 +.nf + #define OtherError (1+ERROR_OFFSET) + typedef struct { + String errorstring; + } OtherErrorArgs; +.fi +.sp 1 +Note that ERROR values must be specified in the range 0 to 65535, but +that they produce a symbol which appears to the C programmer +with values of type int +in the range 65536 to 131171 (in some contexts, the user may wish to +treat C error values in the range 0 to 65535 as Unix error numbers). +The structure corresponding to the error +may be useful in creating and referencing error arguments +in C functions. +.sh 2 "Scope of Names" +.pp +Given a courier program beginning +.sp + Example : PROGRAM 537 VERSION 1 = +.sp +types and constants declared in this program belong to the module ``Example1''; +Their full names have prefixed to them the program name and version number. +Given the declaration of ``minInt'' above, the XNS Courier compiler actually +produces a header file (here +.i Example1.h ) +containing the C declaration: +.sp 1 +.nf +static Integer Example1_minInt = {-2147483648}; +.fi +.sp 1 +.pp +This prefixing implies that a C programmer may refer to several different +remote Courier programs without risk of name collision. +For programmer convenience a second header file (here +.i Example1_defs.h ) +is also created by the compiler containing macro definitions which widen +the scope to ``Example1'' and allow the programmer to refer to types and +constants without explicit qualification. The programmer may include +whichever header file he prefers. +.pp +Note that types and constants obtained from a DEPENDS UPON inclusion must +be referenced using fully qualified form. Note also that structure members, +procedure arguments, and enum tags are not currently qualified; beware +potential name conflicts! +.sh 1 "Running the Courier Compiler" +.pp +The specification file is expected to have the extension +.i ".cr" . +Consider the following skeletal Courier program definition: +.sp + Example : PROGRAM 537 VERSION 1 = BEGIN ... END. +.sp +The name of this Courier program is ``Example''; it is version 1. +By convention, this specification would stored in the file +.i "Example1.cr" . +The ``537'' in this example is illustrative only; +the program number is uniquely allocated from the range of LongCardinals +by Xerox. See Appendix B of the Courier standard for program number +assignment procedures. +.pp +The first step is to use the Courier compiler on the specification. +Assuming +.i "xnscourier" +is in your path (it is +.i "/usr/new/xnscourier" +on the Berkeley distribution), type +.sp 1 + xnscourier Example1.cr +.sp 1 +If there are no errors in compilation, +the following files will be produced: +.(b +.TS +c c +l l. +File Contents + +Example1_defs.h scope widening macros (includes Example1.h) +Example1.h definitions, typedefs, and constant declarations +Example1_support.c routines to map between C and Courier +Example1_server.c server main program and support routines +Example1_client.c client routines to support applications calling Example +.TE +.)b +.pp +The header file +.i "Example1_defs.h" +should be included via +.sp + #include ``Example1_defs.h'' +.sp +in all user-written parts of the Courier program +(i.e., the implementations of the client program and server procedures.) +.pp +The +.i "\fB-I\fIinclude-directory" +switch may be used on the command line of +.i xnscourier +to establish a search path for Courier specification files named in a +DEPENDS UPON clause. By default, the compiler looks first in the current +directory followed by the courier-description-file named in the +.i /etc/Courierservices +file. +.sh 2 "Calling Remote Procedures \- Client Implementation" +.pp +A remote procedure appears to the C programmer to look almost like +a local procedure (although it typically makes use of resources not +available on the local machine or else runs many times slower +than would a local procedure). A C client program (i.e. the caller +of a remote procedure) actually calls a stub function generated by +the courier compiler, which in turn executes the code necessary to +invoke the corresponding procedure on the remote machine. +.pp +Before calling the remote procedure it is necessary to establish a +Courier SPP connection (in the process binding a local socket to +a remote machine). This may be done by the calling the function +CourierOpen(), passing it as argument a structure identifying the remote +host. This structure +is of type ``ns_addr'' +as defined in the include file +.i "#include " . +CourierOpen() returns an anonymous pointer +(of type CourierConnection*) to a structure containing +the socket to be used for this courier call, which should be passed +to each remote procedure to be executed on the particular remote host. +CourierOpen() returns NULL if there is a problem opening an SPP connection +to the host specified. +.pp +For low-level access to ns_addr structures, +the procedure getXNSaddr() is available, taking a string in the form +``network#a1.a2.a3.a4.a5.a6#socket'' (where all numbers are hex) or +``networkhigh-networklow#d1-d2-d3-d4#socket'' (where all numbers are +3-digit decimal numbers) +and returning a pointer to the +corresponding ns_addr structure. This result may then be passed as the +argument to CourierOpen(). +More normally, the user will have a string in the form of an NS address, +e.g. ``jqj:computer\ science:cornell-univ''. This string can be +coerced into a Clearinghouse ObjectName, then used in a Clearinghouse-based +lookup to find the corresponding address: +.sp 1 +.nf + Clearinghouse2_ObjectName hobj, hdefault; + struct ns_addr * host; + char * string; + + CH_NameDefault(&hdefault); /* get defaults */ + hobj = CH_StringToName(string, &hdefault); + host = CH_LookupAddr(hobj, 0); +.fi +.sp 1 +.pp +The SPP connection may be reused by multiple RPCs destined for the same +host, even if the procedures are part of different remote programs. However, +the SPP connection may not be reused while an RPC is outstanding; thus, +if you need to issue a Courier call during a BDT transfer, you will need +to obtain a second CourierConnection. +.pp +The client then calls the compiler-generated stub function to invoke +the remote procedure. This stub +function takes at least 2 arguments: +.ip (1) +The pointer returned from CourierOpen(). +If the SPP connection does not +currently exist (i.e. has been closed by the server) +it is reopened for this call. +.ip (2) +A pointer to a user-supplied function which will be used if a Bulk Data +Transfer is required (see the section on BDT below); if this remote +procedure does not involve Bulk Data Transfer, then the function pointer +should be 0 or NULL. +.ip (3...) +One additional argument corresponding to each parameter in the Courier +language description of this procedure. +.pp +The remote procedure returns in one of 2 ways: either it returns +normally, passing back a structure corresponding to the RETURNS parameters +specified in the procedure description, or it signals an error which +may be caught using the DURING ... HANDLER mechanism described in +.i except(1) . +.pp +A Courier procedure is allowed to return multiple results, a language feature +not easily mapped into C. To provide this functionality, all UNIX +courier remote procedures actually return a structure +containing the procedure results. Thus, for a procedure named +Foo, declared +.sp 1 + Foo: PROCEDURE [ ] RETURNS [str: STRING]; +.sp 1 +the C function Foo would return a structure of type FooResults, declared +.sp 1 +.nf + typedef struct { + String str; + } FooResults; +.fi +.fi +.pp +Instead of returning a value of type determined by the RESULTS clause +of a PROCEDURE declaration, a Courier remote procedure call may return +a reject message indicating the remote system's inability to even +attempt a remote operation, or an abort message raising a remote error, +i.e. reporting the operation's failure. Reject and abort messages are +handled by a signalling mechanism or if uncaught cause termination of +the client program. For example, a client calling the remote procedure +defined by +.sp 1 +.nf + NoSuchFile: ERROR = 0; + OtherError: ERROR [ errorstring: STRING ] = 1; + Example: PROCEDURE [ ] REPORTS [ NoSuchFile, OtherError ]; +.fi +.sp 1 +might be coded as +.sp 1 +.nf + conn = CourierOpen(destaddr); + DURING Example(conn,NULL) + HANDLER switch(Exception.Code) { + case REJECT_ERROR: + fprintf(stderr,"Remote reject.\en"); + exit(1); + case NoSuchFile: + fprintf(stderr,"No such file\en"); + break; + case OtherError: + fprintf(stderr,"Remote error %s\en", + CourierErrArgs(OtherErrorArgs,errorstring) ); + break; + case PROTOCOL_VIOLATION: + case INTERNAL_ERROR: + fprintf(stderr,"Local internal error %s\en", + Exception.Message); + break; + default: + fprintf(stderr,"Unknown error, type %d\en", + Exception.Code); + } + END_HANDLER +.fi +.sp 1 +The error value, Exception.code, will be one of (1) ``REJECT_ERROR'', +which indicates a REJECT message from the remote server, (2) a program-defined +ERROR value (offset by ERROR_OFFSET), (3) or ``INTERNAL_ERROR'' +or ``PROTOCOL_VIOLATION'' +indicating +a serious internal error in the Unix courier implementation; in the third +case, a string is available as Exception.Message. +Both errors and rejections may have arguments, which can be accessed +within a handler as a struct pointed to by Exception.Message. For +programmer convenience a macro, CourierErrArgs, is defined, taking +as arguments the typedef defining the current error's arguments +and the name of the argument to be accessed; this macro may be used +only within an error handler. +For rejection messages, the type should be ``rejectionDetails,'' +as defined on page 25 of the Courier spec. +.pp +When done with a particular remote connection, the client should call +CourierClose() with argument the pointer returned from CourierOpen() +to free the socket and cleanly close the connection. +.pp +The client program should be loaded with +.i "Example1_client.o" , +.i "Example1_support.o" , +and -lcourier (the XNS Courier library) +to produce an executable program. +.sh 2 "Writing a Courier Server" +.pp +The Courier protocol specifies a standard for communicating +parameters and results which has been adhered to in this +implementation. +It also specifies an initial connection protocol +and a format for messages. +.pp +There is a single Courier Daemon per UNIX machine whose function +is to listen on a well-known XNS port for Courier interface activation +requests. +A request contains the number of the Courier program +whose server is to be activated, and its version number. These two +numbers are used as a key in the file +.i /etc/Courierservices +to identify +the executable file associated with the particular courier program. +.i /etc/Courierservices +consists of a sequence of lines of the form +.sp 1 + program-name program-number version courier-description-file server-binary-file +.sp 1 +The daemon +either spawns the executable file +or replies with a reject message. Note that each incoming remote procedure +call may be serviced by a separate UNIX process. +This implies that connection-oriented services (services which consist of a +sequence of related procedure calls) must be implemented by maintaining +the state of connections in a file. +.pp +The executable file containing the server for a particular remote +program consists of a main program generated by the courier compiler +which interprets CALL messages from the remote client and calls +one or more user-written functions to implement the +courier procedures. +.pp +Given a courier specification for the remote program Example containing +remote procedure Example, +the server functions (including the C function Example and any support +routines needed) +should be loaded with +.i "Example1_server.o" , +.i "Example1_support.o" , +and -lcourier +to produce the server process that will be invoked whenever +an activation request arrives. +.pp +A server function receives an argument list similar to that passed by +a client to a remote procedure. The first two arguments in the +parameter list should be ignored; each succeeding argument corresponds +to one parameter in the courier declaration. +.pp +To return from a server function you must return a structure containing +the results of the procedure. If this structure contains pointers (e.g. +Strings) you should insure that the data pointed to is allocated +staticly or on the heap rather than on the stack. +You must not call exit(3) from within the server function; doing so will +cause the client (remote caller) to hang indefinitely waiting for a +reply. +.pp +To abort a server function, returning an error code, use the +``raise(code, msg)'' +library function, with arguments the error code and either NULL (if the +error takes no arguments) or a pointer to a structure containing the +arguments to the error. Raise causes a longjump out of the function +directly to the handler, and thence to the remote procedure caller. +For example, to return the OtherError message defined above one might code +.sp 1 +.nf + OtherErrorArg randomerr; + . . . + randomerr.errorstring = sys_errlist[errno]); + raise(OtherError,(char*) &randomerr); +.fi +.sp 1 +.pp +When a server function returns to its main program caller the main() +sends the appropriate RETURN or ABORT message to the remote client, +and does an exit(0). The server then holds the connection for up to 90 +seconds waiting for another Courier call to arrive on the same SPP +stream. +.sh 2 "Dynamic Allocation" +.pp +One additional +.i caveat +is necessary when using Courier remote procedure calls from C: C does not +have garbage collection, so you should free any dynamically allocated storage +when you are done with it. In general, top level Courier objects are +always allocated from the stack or statically; however, strings and sequences +within an object are generally allocated from the heap using malloc(). +.pp +A client program which calls the remote procedure Foo defined above, +returning a STRING, +actually has returned to it a C struct containing a pointer to an array +of malloced characters. When done with this string, the program may free +it by calling clear_FooResults() with argument the address of the +result returned by the call to Foo. Thus: +.sp 1 +.nf + FooResults result; + . . . + result = Foo(conn,NULL); + . . . + printf("result was %s\n",result.str); + clear_FooResults(&result); +.fi +.sp 1 +.sh 1 "Using Bulk Data Transfer" +.pp +.sh 2 "Overview" +.pp +When a Courier program needs to transfer an arbitrary amount of +information as an argument or result of a Courier procedure, the +procedure is usually defined to have an argument of type ``BulkData.Sink'' +or ``BulkData.Source'' (and a ``DEPENDS UPON BulkData'' +is included in the program). +The argument is a ``source'' if it is information transferred from caller +to server (as though a procedure argument), a ``sink'' if it is +information transferred from server to caller (as though a procedure +result). In this +implementation, a Courier procedure may have at most one such argument, +which must have the value null or immediate. +In a Courier call, the bulk data is transmitted in a special way, +multiplexed into the same data path as control messages +between the arguments and the results. +.pp +The BDT user should include in the +.i ".cr" +file a ``DEPENDS UPON BulkData (0) VERSION 1'', then reference these +arguments as BulkData.Source and BulkData.Sink in PROCEDURE +declarations. +The declaration of a Courier PROCEDURE can then indicate a bulk +data transfer by including in its formal argument list a variable +of type BulkData.Source or BulkData.Sink +as appropriate. +The corresponding parameter to the C procedure will be of type +BulkData1_Descriptor. +.sh 2 "Coding a Client" +.pp +To use Bulk Data in a client program, the Courier definition of the +procedure must include one BulkData.Source or BulkData.Sink parameter. +As the actual argument to the call, the C client passes a constant, +either BulkData1_immediateSource, BulkData1_immediateSink, BulkData1_nullSource, +or BulkData1_nullSink as appropriate. The client +also specifies a pointer to a user-supplied function as the second +argument to the remote procedure. +Courier sets up the transaction, then calls the supplied function with +one argument, the pointer returned by CourierOpen() describing the +SPP socket on which to write (if a source argument) or read +(if a sink) the bulk data. +.pp +To complete the transaction normally, the +function should send packets of SPP data terminated by +an end-of-message (if writing) or read up to but not beyond an +end-of-message (if reading), and should return normally to its caller. +To do so, it should call the procedures BDTread(), BDTwrite(), or +BDTclosewrite() +as appropriate. BDTread() and BDTwrite() take arguments analogous to read() +and write() except that instead of a file descriptor they accept the SPP +descriptor returned by CourierOpen(); their arguments are thus +(1) the pointer to the SPP data structure, +(2) a pointer to an array of characters (the IO buffer), +and (3) a count; +they return the number of characters actually read or written. +.pp +The UNIX program can finish a write transfer by calling BDTclosewrite() or +BDTabort(), each with the SPP descriptor as argument. +BDTclosewrite() produces an SPP end-of-message, while BDTabort() instructs +Courier to discard any buffered data and +send a Bulk Data Abort to abort the transaction. A read transfer may also be +prematurely ended by calling BDTabort(). +.sh 2 "Coding a Server" +.pp +Writing a server that uses BDT +is similar to writing the BDT function in a caller. +The server is passed a BulkData.Source or BulkData.Sink (the two are +indistinguishable at run time). +The server routine should check to make sure that the source or sink +is of type null or immediate (active and passive descriptors are not +supported in this implementation) by means of code such as: +.sp 1 +.nf +Foo(bdtconnection,ignoredarg,..., s, ...) + CourierConnection *bdtconnection; + BulkData1_Sink s; + . . . + switch (s.designator) { + case active: + case passive: + /* can't raise BulkData1_InvalidDescriptor, so */ + raise(someerror); + case null: + /* handle null transfer */ + break; + case immediate: + /* handle normal, i.e. immediate, transfer */ + break; + } +.fi +.sp 1 +Note that the BDT error InvalidDescriptor cannot be used unless the +procedure declaration explicitly notes that this is a valid REPORTS error +type. Normally, each courier program defines its own error to be reported +on BDT problems. +.pp +Use the first argument to the server procedure as the handle on the +BDT connection. +As with a client BDTabort() may +be used to send abort messages. +.sh 2 "Sending Bulk Data" +.pp +Sending bulk data, either as client or server, is quite +straightforward. Use the supplied procedure BDTwrite(), whose +semantics are similar to write(). BDTwrite() will return -1 +if an error occurs or an abort from the listener is received; in this +case, the sender should immediately cease transmitting BDT data, and +should instead call BDTabort(). +Typical code to send data on ``bdtconnection'', +in this case read from a buffered file open +as ``source,'' might appear as: +.sp 1 +.nf + CourierConnection *bdtconnection; + FILE *source; + int count; + char buffer[SPPMAXDATA]; + ... + while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 && + BDTwrite(bdtconnection,buffer,count) >= 0 ) + ; + if (count <= 0) /* succsfull transfer */ + BDTclosewrite(bdtconnection); + else + BDTabort(bdtconnection); +.fi +.sp 1 +.pp +Each BDTwrite() transmits one packet after checking for an abort message +from the receiver. +A sender which gets an abort from a remote receiver must immediately +stop transferring data and instead must echo an abort via BDTabort() +to acknowledge the end of the transfer. +.pp +Also, for efficiency's +sake it is desirable to write packets as near as possible to the +nominal maximum length of an SPP packet; this suggests a buffer length of +534 bytes. For programmer convenience, the file +.i courier.h +defines the symbol SPPMAXDATA as 534. +.sh 2 "Receiving Bulk Data" +.pp +To make receiving bulk data simple, the routine BDTread() performs checks for +various conditions on receipt of each packet. BDTread() will return the +count of the number of bytes actually read in each packet (skipping empty +packets), or 0 to indicate end of data. If an error or BDT abort message +occurs, BDTread() will return -1; if this occurs, the receiver should +immediately stop reading BDT data. +.pp +Typical code for receiving bulk data on the socket described by +``bdtconnection'' and +depositing it in the file opened for buffered ouput as ``destfile'' +might be: +.sp 1 +.nf + CourierConnection *bdtconnection; /* BDT source descriptor */ + FILE *destfile; /* output file */ + char sppdata[SPPMAXDATA]; + ... + count = BDTread(bdtconnection, sppdata, SPPMAXDATA); + while (count > 0) { /* actually read data */ + if (fwrite(sppdata, 1, count, destfile) == 0) { + /* error while writing */ + BDTabort(bdtconnection); + break; + } + count = BDTread(bdtconnection, sppdata, SPPMAXDATA); + } + if (count == 0) { + /* successful */ + ... +.fi +.sp 1 +.sh 2 "StreamOf declarations" +.pp +Frequently, data to be sent on a Bulk Data connection will be described +by a StreamOfSomething declaration in the courier specification. For +example, in Clearinghouse several routines are documented as returning +a StreamOfObjectName; unfortunately, a StreamOf declaration is recursive, +and so is not immediately translatable into automatic packing and unpacking +routines which need to know in advance how much space to allocate for the +result. As a kludge, we translate such types as if the recursive +part of the declaration were a null record. +Consider: +.sp 1 +.nf +StreamOfObjectName: TYPE = CHOICE OF { + nextSegment (0) => RECORD [ + segment: SEQUENCE OF ObjectName, + restOfStream: StreamOfObjectName ], + lastSegment (1) => SEQUENCE OF ObjectName}; +.fi +.sp 1 +.lp +In the above declaration, the ``restOfStream is effectively ignored. +Given this declaration, the following routine may be passed to a +remote procedure as the Bulk Data actor. +.sp 1 +.nf +#include "Clearinghouse_support.c" /* get internalize_* routines */ +#define MAXPACKS 5 + +GetData(conn) + CourierConnection *conn; +{ + int count, i; + Unspecified buffer[MAXWORDS*MAXPACKS], *bp, *bufend; + StreamOfObjectName obnames; + + bufend = buffer; + bp = buffer+MAXWORDS*(MAXPACKS-1); /* end of available space */ + while (count = BDTread(conn, (char*)bufend, + MAXWORDS*sizeof(Unspecified)) + ) { + bufend += count/sizeof(Unspecified); + if (bufend > bp) { + fprintf(stderr,"BDT read too big to fit\en"); + BDTabort(conn); + /* should clear out stuff here if we knew how much */ + } + } + bp = buffer; + while (bp < bufend) { + bp += internalize_StreamOfObjectName(&obnames,bp); + if (0 == (int) obnames.designator) + for (i = 0; i < obnames.nextSegment_case.segment.length; i++) + ProcessObjectName( + obnames.nextSegment_case.segment.sequence[i]); + else { + for (i = 0; i < obnames.lastSegment_case.length; i++) + ProcessObjectName( + obnames.lastSegment_case.sequence[i]); + return; + } + } +} +.fi +.sp 1 +.lp +Note that this code is very awkward, and that it requires that the whole +bulk data transfer be stored in memory before it is unpacked. Stay +tuned; we intend to modify the compiler to make the handling of such +streams much easier, at the cost of incompatibility with the existing +scheme, of course! +.sh 2 "Encapsulated Protocols" +.pp +Some courier programs use what might be termed ``encapsulated protocols'' +as a method of type punning to escape from the restrictions of the Courier +language. For example, in Filing +.sp 1 +.nf + Attribute: RECORD [ + type: AttributeType, value: SEQUENCE OF UNSPECIFIED ]; + checksum: AttributeType = 0; + Checksum: TYPE = CARDINAL; + createdBy: AttributeType = 1; + CreatedBy: TYPE = User; +.fi +.sp 1 +Depending on the value of the type subfield, the data in the value field +is intended to be interpreted as either a CARDINAL or a User name. +.pp +Such encapsulation is in general bad design unless a very compelling +need for it exists. It is also awkward to support in a C program. The +best way to handle such types is to write functions to code and decode +the encapsulated value using the low level packing and unpacking routines +provided by the Courier compiler. For example: +.sp 1 +.nf + Filing4_User + AttrToUser(attr) + Filing4_Attribute *attr; + { + Unspecified buf[2049], *bp; /* space for biggest attribute */ + Cardinal len; + Filing4_User retval; + + /* useful fact: Item: TYPE = SEQUENCE OF UNSPECIFIED; */ + externalize_Clearinghouse2_Item(&(attr->value), buf); + bp = buf; + bp += internalize_Cardinal(&len, bp); + bp += internalize_Filing4_User(&retval, bp); + return(retval); + } + + UserToAttr(id, attr) + Filing4_User id; + Filing4_Attribute *attr; + { + Unspecified buf[2049], *bp; + Cardinal len; + + /* don't know length yet, so leave space for it */ + bp = buf + sizeof_Cardinal(len); + len = externalize_Filing4_User(&id, bp); + (void) externalize_Cardinal(&len, buf); + internalize_Clearinghouse2_Item(&(attr->value), buf); + return; + } + +.fi +.sp 1 +Since the SEQUENCE OF UNSPECIFIED may have undergone arbitrary +transformations +during deserialization, translating it to a User record consists of first +reserializing it into an array of char, then deserializing it using the +low-level routine appropriate to the encapsulated type. Translating to +an encapsulated type is an almost precise inverse. +.sh 1 "Example I: Passwd.cr" +.pp +This section contains a Courier program which implements remote lookup in +.i "/etc/passwd" +(the UNIX database of user names, passwords, home directories, and so +on). +It does not utilize Bulk Data Transfer, but does illustrate most other +features of this courier implementation. +The applications programmer would first write +.i PasswordLookup.cr , +the description of the courier interface for the service, then might +write +.i PasswordLookup.c , +containing the routines needed to implement a server for this courier +program, or might write +.i lookup.c , +a typical client of this service. +.bp +.sh 2 "The specification (PasswordLookup.cr)" +.sp 1 +.nf +PasswordLookup : PROGRAM 754 VERSION 1 = + +BEGIN + + -- This is a translation of the passwd structure in + + Passwd : TYPE = RECORD [ + pw_name, pw_passwd : STRING, + pw_uid, pw_gid, pw_quota : LONG CARDINAL, + pw_comment, pw_gecos, pw_dir, pw_shell : STRING + ]; + + -- Remote Errors + + NoSuchUser : ERROR = 0; + OtherError : ERROR [ errorstring: STRING ] = 1; + + -- Remote entry points. + + LookupUid : PROCEDURE [ uid : CARDINAL ] RETURNS [ passwd : Passwd ] + REPORTS [ NoSuchUser ] + = 0; + + LookupUser : PROCEDURE [ user : STRING ] + RETURNS [ passwd : Passwd, forward : STRING ] + REPORTS [ NoSuchUser, OtherError ] + = 1; + +END. +.fi +.sp 2 +.sh 2 "PasswordLookup_defs.h" +.sp 1 +.nf +/* + * Declarations for Courier program PasswordLookup. + */ +#include +#include + +typedef struct { + String pw_name; + String pw_passwd; + LongCardinal pw_uid; + LongCardinal pw_gid; + LongCardinal pw_quota; + String pw_comment; + String pw_gecos; + String pw_dir; + String pw_shell; +} Passwd; + +#define NoSuchUser 0 + +#define OtherError 1 +typedef struct { + String errorstring; +} OtherErrorArg; + +typedef struct { + Passwd passwd; +} LookupUidResult; + +typedef struct { + Passwd passwd; + String forward; +} LookupUserResult; + +extern LookupUidResult LookupUid(); + +extern LookupUserResult LookupUser(); +.fi +.sp 2 +.sh 2 "Makefile" +.sp 1 +.nf +CFLAGS = -O +USEROBJS = lookup.o PasswordLookup_client.o +SRVROBJS = PasswordLookup.o PasswordLookup_server.o +LIBS = -lcr +DESTDIR = /usr/lib/courier + +all: lookup PasswordLookup + +lookup: $(USEROBJS) + cc -o lookup $(USEROBJS) $(LIBS) + +PasswordLookup: $(SRVROBJS) + cc -o PasswordLookup $(SRVROBJS) $(LIBS) + +$(USEROBJS) $(SRVROBJS): PasswordLookup_defs.h + +PasswordLookup_defs.h \e +PasswordLookup_server.c \e +PasswordLookup_client.c: PasswordLookup.cr + courier PasswordLookup.cr + +install: all + install -s PasswordLookup $(DESTDIR) + +clean: + -rm -f *.o PasswordLookup_*.c PasswordLookup_defs.h +.fi +.bp +.sh 2 "The server procedures (PasswordLookup.c)" +.sp 1 +.nf +#include +#include "PasswordLookup_defs.h" + +extern Passwd *getpwnam(), *getpwuid(); + +LookupUidResult +LookupUid(CourierConnection,CourierBDTProc,uid) + CourierConnection *CourierConnection, CourierBDTProc; + Cardinal uid; +{ + Passwd *pw; + + pw = getpwuid(uid); + if (pw == NULL) + raise(NoSuchUser,NULL); + else { + return (*(LookupUidResult*) &pw); + } +} + +LookupUserResult +LookupUser(CourierConnection,CourierBDTProc,user) + CourierConnection *CourierConnection, CourierBDTProc; + String user; +{ + LookupUserResult result; + Passwd *pw; + FILE *fwdfd; + static char forward[100]; + + pw = getpwnam(user); + if (pw == NULL) + raise (NoSuchUser,NULL); + else { + sprintf(forward,"%s/.forward",pw->pw_dir); + if ((fwdfd = fopen(forward,"r")) == NULL) { + forward[0] = '\e0'; + else { + fgets(forward,100,fwdfd); + fclose(fwdfd); + if (strlen(forward) < 2) ( + static OtherErrorArg randomerr = { + "invalid forwarding file"}; + raise(OtherError,&randomerr)); + } + result.password = *pw; + result.forward = forward; + return (result); + } +} +.fi +.bp +.sh 2 "The user program (lookup.c)" +.sp 1 +.nf +/* + * Sample program to access remote password lookup. + * Usage: lookup machine username + */ +#include +#include "PasswordLookup_defs.h" + +main(argc, argv) + int argc; char **argv; +{ + LookupUserResult result; + Passwd passwd; + CourierConnection *connection; + + if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) { + fprintf(stderr,"Usage: %s machine file ...\en",argv[0]); + exit(1); + } + if ((connection = CourierOpen(destaddr)) == NULL) { + fprintf(stderr,"Can't open connection to %s\en",argv[1]); + exit(1); + } + DURING + result = LookupUser(connection,NULL,argv[2]); + HANDLER + switch(Exception.Code) { + case Courier_reject: + fprintf("Connection rejected, code = %d\en", + CourierErrArgs(rejectionDetails,designator) ); + exit(1); + case NoSuchUser: + printf("User %s unknown on %s.\en", argv[2], argv[1]); + exit(0); + case OtherError: + fprintf(stderr,"Remote error %s\en", + CourierErrArgs(OtherErrorArg,errorstring) ); + exit(1); + } + END_HANDLER; + + displaypwd(& result.passwd); + displayfwd(result.forward); + CourierClose(connection); +} + +displaypwd(p) + Passwd *p; +{ + printf("%s:%s:%d:%d:%s:%s:%s\en", + p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, + p->pw_gecos, p->pw_dir, p->pw_shell); +} + +displayfwd(s) + String s; +{ + if (*s) printf("Mail forwarding to %s\en",s); + else printf("Mail is not forwarded\en"); +} +.fi +.bp +.sh 1 "Example II: PrintFile" +.pp +This example is a very simple application of Bulk Data Transfer. +.sh 2 "PrintFile.cr" +.sp 1 +.nf +PrintFile: PROGRAM 756 VERSION 1 = +BEGIN + + DEPENDS UPON BulkData (0) VERSION 1; + + -- Remote errors. + + CantPrint: ERROR = 0; + + -- Remote entry points. + + RPrint: PROCEDURE [ s: BulkData.Source ] + REPORTS [ CantPrint ]; + +END. +.fi +.sp 1 +.sh 2 "A typical client (remoteprint.c)" +.lp +.sp 1 +.nf +/* + * Sample proram to print a file remotely using trivial remote print + * protocol. + * Usage: remoteprint filename + * + */ +#include +#include +#include /* for XNS addresses */ +#include "PrintFile_defs.h" + +static FILE * source; /* communicate from main to SendSource */ + +main(argc, argv) + int argc; + char *argv[]; +{ + CourierConnection *connection; + struct ns_addr *destaddr; + + if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) { + fprintf(stderr,"Usage: %s machine file ...\en",argv[0]); + exit(1); + } + if ((connection = CourierOpen(destaddr)) == NULL) { + fprintf(stderr,"Can't open connection to %s\en",argv[1]); + exit(1); + } + argv++; + while (argc-- > 2) { + argv++; + if (strcmp(argv[0],"-") == 0) source = stdin; + else source = fopen(argv[0],"r"); + if (source == NULL) + fprintf(stderr,"Can't open %s\en",argv[0]); + else DURING + RPrint(connection,SendSource,immediateSource); + HANDLER + fprintf(stderr,"Call to RPrint failed.\en"); + END_HANDLER; + fclose(source); + } + CourierClose(connection); +} + +SendSource(bdtconnection) +CourierConnection *bdtconnection; +{ + int count; + char buffer[SPPMAXDATA]; + + while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 && + BDTwrite(bdtconnection,buffer,count) >= 0 ) + ; + if (count <= 0) + BDTclosewrite(bdtconnection); /* last packet with EOM set */ + else + BDTabort(bdtconnection); +} +.fi +.sp 2 +.sh 2 "Server (PrintFile.c)" +.sp 1 +.nf +#include +#include +#include +#include "PrintFile_defs.h" + +RPrintResult +Rprint(source,CourierBDTProc,s) + CourierConnection *source, CourierBDTProc; + BulkData1_Source s; +{ + FILE *printpipe; + char sppdata[SPPMAXDATA]; + + switch (s.designator) + case active: + case passive: + raise(CantPrint); + /*NOTREACHED*/ + case null: + system("print /dev/null"); /* print a null file */ + return; + case immediate: + if ((printpipe = popen("print","w")) == NULL) { + raise(CantPrint); + /*NOTREACHED*/ + } + count = BDTread(source, sppdata, SPPMAXDATA); + while (count > 0) { /* actually read data */ + if (fwrite(sppdata, 1, count, printpipe) == 0) { + BDTabort(source); + break; + } + count = BDTread(source, sppdata, SPPMAXDATA); + } + if (pclose(printpipe) == 0 && count == 0) + return; + else raise(CantPrint); +} +.fi +.sh 1 "One Final Example" +.pp +Finally, we present a slightly +more useful example, a program to print an Interpress +file on a Services-8 printer. It depends on the standard Xerox Printing +specification, program 4 version 3. For an even better example, look +at the printing client, xnsprint, provided with the XNS Courier distribution +as +.i "./examples/print/xnsprint.c" . +.sp 2 +.nf +#include +#include +#include +#include "Printing_defs.h" +#include + +static FILE *ipfile = NULL; + +SendSource(bdtconnection) +CourierConnection *bdtconnection; +{ + int count; + char buffer[SPPMAXDATA]; + + while ( (count = fread(buffer,1,SPPMAXDATA,ipfile)) > 0 && + BDTwrite(bdtconnection,buffer,count) >= 0 ) + ; + if (count <= 0) + BDTclosewrite(bdtconnection); /* last packet with EOM set */ + else + BDTabort(bdtconnection); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + PrintResults result; + struct ns_addr *destaddr; + CourierConnection *conn; + extern struct ns_addr *getXNSaddr(); + PrintAttributes attributes; + PrintOptions options; + + /* use Cornell print server, CornellS1 (slander) */ + destaddr = getXNSaddr("2-273#2-852-159-207"); + attributes.length = 0; + options.length = 0; + if (argc != 2 || ((ipfile = fopen(argv[1],"r")) == NULL)) { + fprintf(stderr,"Usage: %s file\en",argv[0]); + exit(1); + } + if ((conn = CourierOpen(destaddr)) == NULL) { + fprintf(stderr,"Can't open connection to %s\en",xnshost); + exit(1); + } + + DURING + result = Print(conn, SendSource, BulkData1_immediateSource, + attributes, options); + HANDLER { + switch (Exception.Code) { + case Busy: + fprintf(stderr,"Busy\en"); + break; + case ConnectionError: + fprintf(stderr,"Connection error, %d\en", + CourierErrArgs(ConnectionErrorArgs,problem)); + break; + case InsufficientSpoolSpace: + case SpoolingQueueFull: + fprintf(stderr,"Insufficient spool space\en"); + break; + case SpoolingDisabled: + fprintf(stderr,"Spooling disabled\en"); + break; + case MasterTooLarge: + case TooManyClients: + case ServiceUnavailable: + case SystemError: + case InvalidPrintParameters: + case MediumUnavailable: + case TransferError: + fprintf(stderr,"Some Printing error, number %d\en", + Exception.code-ERROR_OFFSET); + break; + case Undefined: + fprintf(stderr,"Undefined error, number %d\en", + CourierErrArgs(UndefinedArgs,problem)); + break; + case REJECT_ERROR: + fprintf(stderr,"REJECT: type = %d\en", + CourierErrArgs(rejectionDetails, designator)); + break; + default: + fprintf(stderr,"Some random error, code %d\en", + Exception.Code); + break; + } + exit(1); + } END_HANDLER; + + CourierClose(conn); + printf("Done. Request ID %x %x %x %x %x\en", + result.printRequestID[0], result.printRequestID[1], + result.printRequestID[2], result.printRequestID[3], + result.printRequestID[4]); +} +.fi +.bp +.sh 1 "Final Notes" +.pp +The issues of authentication and protection are +difficult. +They are only touched upon in this implementation. +Currently, each Courier program must perform any authentication +(presumably using the Authentication protocol) +if this is desired. A few routines exist to allow cleints and servers +to perform simple Authentication; no support for strong Authentication +has yet been written. +.pp +This implementation is fairly inefficient, especially in +the implementation of servers. Courier calls require substantial +extra copying of courier arguments and results. More significantly, +the requirement that each call spawn a unique process is very expensive +in UNIX, and should be reconsidered. +A sequence of RPCs on the same SPP stream that specifies the same program +and version number uses the same process, but in all other cases a new +process must be spawned for each individual RPC. +.pp +This implementation defines a number of reserved identifiers. +.b ">>>should list them here<<<" +.sh 1 "Appendix" +.pp +This appendix contains the grammar for the Courier language. +It is similar to the YACC specification used +by the Courier compiler. +.sp +.ps -2p +.vs -2p +.nf +.TS +l l l l l. +%token identifier number string + ARRAY BEGIN BOOLEAN CARDINAL + CHOICE DEPENDS END ERROR + FALSE INTEGER LONG OF + PROCEDURE PROGRAM RECORD REPORTS + RETURNS SEQUENCE STRING TRUE + TYPE UNSPECIFIED UPON VERSION +.TE +%% + +Program : + identifier ':' PROGRAM number VERSION number '=' + BEGIN DependencyList DeclarationList END '.' + | + identifier ':' PROGRAM '=' + BEGIN DependencyList DeclarationList END '.' + ; + +DependencyList : + /* empty */ + | DEPENDS UPON ReferencedProgramList ';' + ; + +ReferencedProgramList : + ReferencedProgram + | ReferencedProgramList ',' ReferencedProgram + ; + +ReferencedProgram : + identifier '(' number ')' VERSION number + ; + +DeclarationList : + /* empty */ + | DeclarationList Declaration + ; + +Declaration : + identifier ':' TYPE '=' Type ';' + | identifier ':' Type '=' Constant ';' + ; + +Type : + PredefinedType + | ConstructedType + | ReferencedType + ; + +PredefinedType : + BOOLEAN + | CARDINAL + | LONG CARDINAL + | INTEGER + | LONG INTEGER + | STRING + | UNSPECIFIED + | LONG UNSPECIFIED + ; + +ConstructedType : + '{' CorrespondenceList '}' + | ARRAY NumericValue OF Type + | SEQUENCE MaximumNumber OF Type + | RECORD '[' FieldList ']' + | RECORD '[' ']' + | CHOICE DesignatorType OF '{' CandidateList '}' + | PROCEDURE ArgumentList ResultList ErrorList + | ERROR ArgumentList + ; + +ReferencedType : + identifier + | identifier '.' identifier + ; + +CorrespondenceList : + Correspondence + | CorrespondenceList ',' Correspondence + ; + +Correspondence : + identifier '(' NumericValue ')' + ; + +MaximumNumber : + NumericValue + | /* empty */ + ; + +NumericValue : + number + | ReferencedConstant + ; + +DesignatorType : + /* empty */ + | ReferencedType + ; + +CandidateList : + Candidate + | CandidateList ',' Candidate + ; + +Candidate : + DesignatorList '=''>' Type + ; + +DesignatorList : + Designator + | DesignatorList ',' Designator + ; + +Designator : + identifier + | Correspondence + ; + +ArgumentList : + /* empty */ + | '[' FieldList ']' + ; + +ResultList : + /* empty */ + | RETURNS '[' FieldList ']' + ; + +ErrorList : + /* empty */ + | REPORTS '[' NameList ']' + ; + +FieldList : + Field + | FieldList ',' Field + ; + +Field : + NameList ':' Type + ; + +Constant : + PredefinedConstant + | ConstructedConstant + | ReferencedConstant + ; + +PredefinedConstant : + TRUE + | FALSE + | number + | '-' number + | '"' string '"' + ; + +ConstructedConstant : + identifier + | '[' ElementList ']' + | '[' ComponentList ']' + | '['']' + | identifier Constant + | number + ; + +ReferencedConstant : + identifier + | identifier '.' identifier + ; + +ElementList : + Constant + | ElementList ',' Constant + ; + +ComponentList : + Component + | ComponentList ',' Component + ; + +Component : + NameList ':' Constant + ; + +NameList : + identifier + | NameList ',' identifier + ; +.fi +.vs +.ps -- 2.20.1