Annotation of 43BSD/contrib/xns/doc/courier.tbl.me, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.