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