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