CSL Notebook Entry: Generating Courier and SUN RPC stubs in Cedar
XEROX
To CedarUsers From Doug Terry PARC/CSL
Subject Generating Courier and SUN RPC stubs in Cedar Date December 31, 1987
Copyright © 1987 by Xerox Corporation. All rights reserved.
FOR XEROX INTERNAL USE ONLY
Abstract I recently generated Courier and SUN RPC stubs for LoganBerry. This collection of notes on what I had to do may be of interest to others that want to build RPC interfaces for other Cedar programs.
Attributes informal, technical, Cedar, Communication, Distributed computing
Generating Courier and SUN RPC stubs in Cedar
Introduction
Cedar now has the ability to communicate over internetworks using various transport protocols (Pup, XNS, and TCP/IP) and their associated remote procedure call (RPC) mechanisms (Cedar RPC, Courier, SUN RPC). To maximize its interoperability with other environments and operating systems, a Cedar-based server should ideally export as many RPC interfaces as possible.
I recently generated Courier and SUN RPC stubs for LoganBerry so that LoganBerry servers can be accessed from XDE, Smalltalk, Lisp, or UNIX machines. LoganBerry servers could already be accessed from other Cedar machines using the Cedar RPC protocol. See LupineUsersGuide.tioga for information on how to automatically generate Cedar RPC stubs from a cedar interface. Following are notes on what I had to do to generate/write the Courier and SUN RPC stubs. Hopefully, this discussion will be of use to others that wish to build such stubs, especially since documentation is notably lacking in this area.
Generating Courier stubs
Courier is both a language for describing an interface, including types and remote operations, and a protocol for communicating those types and invoking those remote operations. Courier is part of the Xerox Network Systems (XNS) protocol family. A complete description on the language and the protocol can be found in "Courier: The Remote Procedure Call Protocol", XSIS 038112, December 1981.
The first step in building Courier RPC stubs is to write a Courier program describing the interface that you want to export. A skeleton Courier program can be found as CourierProgram.form in [Cedar]<CedarChest7.0>Top>Forms.df. The Courier language, not surprisingly, is very similar to Mesa. So, if you already have a Cedar/Mesa interface, then the Courier program can be obtained by simply copying most of the type and procedure declarations and then doing some minor editing. In particular, Courier doesn't have REFs, LISTs, ROPEs, ATOMs, etc. REFs can be replaced with their referenced type. ROPEs and ATOMs can be replaced with STRINGs. For the most part, LISTs can be replaced with SEQUENCEs. For each PROCEDURE declaration, an explicit procedure number must be assigned, and so on.
Each Courier program must also have a unique program and version number. The version number can start at 1, and should be incremented whenever the courier program changes. Program numbers are centrally administered to ensure that they are unique throughout the internet. CSL has been given a block of Courier program numbers that are locally administered by Bill Jackson. See Bill to obtain an official number for your program. Of course, you can simply make up a number while developing your service; but you are better off getting an official number from the start since Sirocco, the stub generator, creates files with the program number in their names.
Given a Courier program, Sirocco will generate the appropriate stubs. These stubs marshall/unmarshall data types, call upon the Courier runtime routines to invoke remote operations, etc. Suppose your Courier program, named "LoganBerry", has program number 2205, version 1, and is stored as LoganBerry.cr. Typing "Sirocco LoganBerry" to a Command Tool generates the following files:
% Sirocco LoganBerry
Files Written:
LoganBerryP2205V1.Tables
LoganBerryP2205V1.Mesa
LoganBerryP2205V1Init.Mesa
LoganBerryP2205V1Aux.Mesa
LoganBerryP2205V1AuxImpl.Mesa
LoganBerryP2205V1ClientImpl.Mesa
LoganBerryP2205V1ServerImpl.Mesa
%
These files play the following roles:
LoganBerryP2205V1.tables -- symbol table
LoganBerryP2205V1.mesa -- cedar interface with type and procedure declarations (all procedures have a CrRPC.Handle as their first argument)
LoganBerryP2205V1Init.mesa -- does nothing (for some Courier programs it helps to initialize constants, etc.)
LoganBerryP2205V1Aux.mesa -- cedar interface declaring procedures that generate ROPE representations for argument data types
LoganBerryP2205V1AuxImpl.mesa -- exports LoganBerryP2205V1Aux.Mesa
LoganBerryP2205V1ClientImpl.mesa -- exports LoganBerryP2205V1.Mesa; implements stubs to be used by clients that import LoganBerryP2205V1 (includes marshalling/unmarshalling procedures and calls to CrRPC.Call)
LoganBerryP2205V1ServerImpl.mesa -- imports LoganBerryP2205V1.Mesa; implements stubs to be used by servers that provide the real implementation of LoganBerryP2205V1
Clients of the service should import LoganBerryP2205V1 (and run LoganBerryP2205V1ClientImpl) while the implementor of the service should export LoganBerryP2205V1 (and run LoganBerryP2205V1ServerImpl). Clients should be prepared to catch CrRPC.Error as well as any errors that might be raised by LoganBerryP2205V1. Clients need to create a CrRPC.Handle to pass to procedure calls; this is done by calling CrRPC.CreateClientHandle with the XNS address of the server. A machine's address can be obtained given its name by calling XNSCH.LookupAddressFromRope. Beware that many Cedar machines are not currently registered in the Clearinghouse.
In my case, I already had an interface, LoganBerry.mesa, that I wanted clients to be able to continue to use. This is the interface that I pass to Lupine to generate the Cedar RPC stubs and from which I copied the declarations for my Courier program. The interface generated by Sirocco, LoganBerryP2205V1.mesa, is very similar to LoganBerry.mesa except for the types I needed to change in the Courier program (e.g. LISTs to SEQUENCEs). Thus, I needed to write two more simple Cedar programs:
LoganBerryToLoganBerryP2205V1.mesa -- imports LoganBerryP2205V1 and exports LoganBerry
LoganBerryP2205V1ToLoganBerry.mesa -- imports LoganBerry and exports LoganBerryP2205V1
In addition to translating calls on one interface into the other, these programs must convert between the data types in the two interfaces. For instance, I needed to write routines that convert between a particular type of LIST and the corresponding SEQUENCE type.
By binding LoganBerryToLoganBerryP2205V1 and LoganBerryP2205V1ClientImpl together, I get a stub that exports the usual LoganBerry interface yet places Courier calls to a remote LoganBerry server. Binding LoganBerryP2205V1ServerImpl, LoganBerryP2205V1ToLoganBerry, and LoganBerryImpl together produces a LoganBerry server that can be run on any Cedar machine and accessed using Courier RPC.
Note: if you want to have authenticated Courier calls, you must explicitly pass XNS credentials as a parameter to each call. Use XNSAuth.mesa for authenticated conversations.
Writing SUN RPC stubs
SUN has its own RPC mechanism that is based on UDP, a datagram protocol used in the DARPA Internet. SUN also has its own External Data Representation (XDR) that dictates how procedure arguments of various types are passed over a network. The XDR formats are similar to the ones used by Courier, but the SUN RPC protocol is quite different.
SUN RPC interfaces, like Courier ones, need program and version numbers; these are passed as arguments to each remote procedure invocation. We have not yet obtained a block of official program numbers from SUN. We (Alan Demers, Bill Jackson, and I) have adopted the convention that the SUN RPC program number is the Courier program number plus 555,000,000 (decimal). That is, if you have a Courier program number such as 2205, then use 555002205 as the SUN RPC program number.
As of now, there is no compiler that generates SUN RPC stubs. Thus, they must be created by hand. SunRPCRuntimeDoc.tioga requests that stub writers adhere to the following conventions:
"For remote program Blort there are Mesa interfaces Blort.mesa, BlortClient.mesa, and BlortServer.mesa. Blort.mesa consists of Mesa type definitions corresponding to the data types and procedures of Blort. BlortClient.mesa and BlortServer.mesa, which are identical except for their names, consist of procedure declarations using the types from Blort.mesa. Clients of Blort import from BlortClient.mesa; the server exports to BlortServer.mesa. A Mesa program BlortClientStub.mesa does serialization/deserialization on the client side. It exports to BlortClient.mesa and invokes the communication primitives defined in SunRPC.mesa. Another Mesa program, BlortServerStub.mesa, does serialization/deserialization on the server side. It registers itself with SunRPC.mesa to be invoked when an RPC call is received, and imports from BlortServer.mesa."
I found that the SUN RPC stubs could be obtained by minor editing of the Sirocco-generated stubs. In particular, I created the following files in the following manner:
LoganBerrySunRPC.mesa -- copy of LoganBerryP2205V1.mesa with the procedure declarations removed (i.e. just the type declarations)
LoganBerrySunRPCClient.mesa -- copy of LoganBerryP2205V1.mesa with only the procedure declarations
LoganBerrySunRPCServer.mesa -- identical to LoganBerrySunRPCClient.mesa (except for the name)
LoganBerrySunRPCClientStub.mesa -- copied from LoganBerryP2205V1ClientImpl.mesa then edited as described below
LoganBerrySunRPCServerStub.mesa -- copied from LoganBerryP2205V1ServerImpl.mesa then edited as described below
Since SUN's XDR is so similar to Courier, the marshalling/unmarshalling routines needed by LoganBerrySunRPCClientStub and LoganBerrySunRPCServerStub can be copied almost verbatim from LoganBerryP2205V1ClientImpl and LoganBerryP2205V1ServerImpl. Of course, calls on CrRPC should be changed to calls on SunRPC. Note, however, that XDR is based on 32-bit quantities whereas Courier uses 16-bit quantities; thus, all references to CARD16 should be replaced by CARD32. For example, change calls on CrRPC.PutCard16 to SunRPC.PutCard32, etc. Hint: using LONG CARDINALs instead of CARDINALs in the Courier program will help to eliminate many of the differences between Courier and XDR.
The Courier and SUN RPC runtimes also differ in the way remote procedures are invoked. Sirocco generates calls in LoganBerryP2205V1ClientImpl that look like the following:
CrRPC.Call[h~h, remotePgm~2205, remotePgmVersion~1, remoteProc~8 , putArgs~PutArgs, getResults~GetResults, getError~GetError];
where PutArgs, GetResults, and GetError are procedures that do what their names suggest. You can simply define a procedure that maps this calling syntax to the one desired by SunRPC and your work is practically done. In fact, here is such a procedure:
Call:
PROC [h: SunRPC.Handle, remotePgm, remotePgmVersion, remoteProc:
CARD, PutArgs: PutArgsProc, GetResults: GetResultsProc, getError: GetErrorProc] ~ {
SunRPC.StartCall[h~h, c~SunRPCAuth.Initiate[], pgm~remotePgm+555000000, version~remotePgmVersion, proc~remoteProc];
PutArgs[h];
[] ← SunRPC.SendCallAndReceiveReply[h~h, timeoutMsec~2000, retries~5];
GetResults[h];
SunRPC.ReleaseReply[h];
};
Note that this calling sequence sends NULL credentials (c~SunRPCAuth.Initiate[]). Use SunRPCAuth.mesa to create real authenticated credentials if you so desire.
On the server side, only minor editing turned LoganBerryP2205V1ServerImpl into LoganBerrySunRPCServerStub. In addition to the afore-mentioned changes to the marshalling routines, calls on beginReturn[h] were simply replaced by SunRPC.StartReply[h]. The SunRPC.ServerProc is almost identical to the CrRPC.ServerProc. The call on CrRPC.RegisterServerProc was replaced with a call to SunRPC.CreateServer. The only thing added to LoganBerrySunRPCServerStub is a call to SunPMapLocal.SetLocal to register the LoganBerry service with the local port mapper.
As with the Courier stubs, I needed two additional programs to map between the LoganBerry and LoganBerrySunRPC interfaces:
LoganBerryToLoganBerrySunRPC.mesa -- copied from LoganBerryToLoganBerryP2205V1.mesa with only minor changes
LoganBerrySunRPCToLoganBerry.mesa -- copied from LoganBerryP2205V1ToLoganBerry.mesa with only minor changes
LoganBerryToLoganBerrySunRPC uses the ArpaName interface to look up the Arpa address of a named machine from a Domain Name server and uses the SunPMapClient interface to obtain the appropriate port number from the Port Mapper. It then calls SunRPC.Create, passing the address and port, in order to get a SunRPC.Handle that can be used to invoke remote operations.
By binding LoganBerryToLoganBerrySunRPC and LoganBerrySunRPCClientStub together, I get a stub that exports the usual LoganBerry interface yet places SUN RPC calls to a remote LoganBerry server. Binding LoganBerrySunRPCServerStub, LoganBerrySunRPCToLoganBerry, and LoganBerryImpl together produces a LoganBerry server that can be run on any Cedar machine and accessed using SUN RPC.
Acknowledgements
Special thanks go to Alan Demers for helping me figure out how to use the SunRPCRuntime facilities. Jim Foote's echo program (/Ivy/Foote/Top/SunEcho.df) was also tremendously useful as sample SUN RPC client and server programs.
Terry.pa 21 Sep 89 16:16:27 PDT RPC using Lupine's interface records
Date: Thu, 21 Sep 89 16:16:27 PDT
From: Terry.pa
Subject: RPC using Lupine's interface records
To: Swinehart, PolleZ
Cc: Terry
The file, [Voice]<Voice>LoganBerry>LoganBerryImportImpl.mesa, contains the old LoganBerry code that hides the details of various RPC protocols in Lupine interface records. You might want to look at this code as an example of what you could do for ThSmarts and such. The advantage of this approach is that existing RPC callers need not change, i.e. they need not be aware that different RPC protocols are being used (provided that information passed as arguments is not protocol-specific). Of course, there are disadvantages too.
LoganBerryImportImpl.mesa has the following peculiarities/features:
— It exports *and* imports LoganBerryRpcControl. It exports LoganBerryRpcControl since all object-oriented RPC calls turn into calls on the RpcControl interface. It imports LoganBerryRpcControl so that it can call the real Lupine stubs when necessary. Importing and exporting the same interface requires some configuration magic.
— The exported ImportNewInterface procedure must decide which protocol to use based on the interfaceName. It does this by looking for strings like "Sun" appended to the interface name. You could come up with a different convention.
— Local calls are handled by crafting an interface record containing procedures that call directly on the LoganBerry interface. ThSmarts probably does something similar now for local calls.
— Calls using protocols other than PUP RPC are handled by crafting an interface record containing procedures that call directly on Courier or Sun RPC stubs. The only difficulty is that you need a handle to make a Sun RPC (or Courier) call. Unfortunately, there is no place in the InterfaceRecord for storing this handle. Thus, I maintain a separate RefTab for associating Sun RPC (or Courier) handles with interface records.
The code is fairly straight-forward. Look at it and let me know if you have any questions. It is all a matter of binding procedure calls to the right procedures. Nothing in the code says anything about RPC packet formats and such. These details are all hidden in the stubs that get generated for you (with any luck).
... Doug