The Remote Procedure Call package Henry Thompson File: rpc.tedit Revised: December 6, 1985 Remote Procedure Calls This package implements the Cedar RPC transport mechanism. Its architecture is copied more or less directly from the Cedar implementation, to which reference should be made in difficulty. It is designed to be used in conjunction with stubs produced by the Interlisp-D version of Lupine (see below) to provide remote service. As it implements not only remote procedure calls, but also remote signalling a la CedarMesa, it depends on the Signals package - SIGNAL, q.v. for more information. To start the RPC world, call InitRPC() - to turn it off, use StopRPC(). LUPINE - Producing stubs The goal of all this is to allow an Interlisp-D system to export services to or import services from remote machines. The file rpc-lupine.dcom implements a stub creation mechanism modeled on the Cedar version (ref. Bruce Nelson's thesis (Xerox PARC report CSL-81-9) and the Lupine User's Guide - [indigo]documentation>LupineUsersGuide.press.). In particular the function Lupine(packageName functionSpecList signalSpecList typeSpecList lupineTypeString noServer noClient) will produce a set of server stubs to interface between the RPC transport mechanisms and a set of vanilla Lisp functions, and a set of client stubs to communicate between callers of those functions and the RPC transport mechanism. This allows the network to intervene between the caller and the callee. The crucial point to comprehend in the RPC world is that the semantics of a remote call are the same as the semantics of a local call - all the effort is directed at hiding the fact that anything out of the ordinary is going on - pay no attention to the man behind the curtain. A trivial example will demonstrate the basic story - suppose we have the following Cedar interface spec: Trivial: DEFINITIONS = BEGIN Triv: TYPE = RECORD[a,b: INTEGER]; Trivial: PROC[arg1: INT, arg2: Rope.ROPE, arg3: Triv] RETURNS[result: BOOLEAN]; TrivialFailure: TYPE = { badNum, badString }; TrivialFailed: SIGNAL[why: TrivialFailure]; END. Translating this with Cedar Lupine will produce client and server interfaces to the RPC transport mechanisms. To plug into those from Lisp, or to plug Lisp to Lisp, we must supply the same information as is contained in the Cedar interface spec. to Lisp. The following records from Lisp Lupine are used to specify an interface: (RECORD FunctionSpec (fn . specs)) (RECORD ArgSpec (argName argType)) (RECORD TypeSpec (typeName typeType)) (RECORD LupineType (typeName . typeParm)) The following are the equivalent for Lisp Lupine of the above interface spec: (RPAQQ TrivialSpec ( (Trivial (arg1 FIXP)(arg2 STRING)(arg3 Triv)(RETURNS BOOLEAN))) (RPAQQ TrivialSignals ( (TrivialFailed (ARGS TrivialFaulure))) (RPAQQ TrivialTypes ( (Triv (RECORD (a SSMALLP)(b SSMALLP)) (TrivialFailure (ENUMERATION badNum badString))) Nearly the same format is used for both functions and signals, since signals are just functions by another name. The semantics of signals are described in signal.tedit, press. Types, which appear in the argType and typeType fields above, are either atomic or of the form given by LupineType, in which case the name is atomic, and the parameters are particular to the named type. Only some primative types take parameters - user-defined types cannot, although of course they can be defined as parameterised types, as in the above example. The built-in types available are {SSMALLP FIXP BOOLEAN STRING ATOM STREAM ENUMERATION ARB BITS LIST RECORD RESULT SIGARGS REF SEQRECORD SEQUENCE}. Of these SSMALLP, FIXP, BOOLEAN, STRING, ATOM and ARB are atomic. STREAM, ENUMERATION, BITS, LIST, RECORD, REF, SEQRECORD, and SEQUENCE are parameterised. RESULT and SIGARGS are parameterised pseudo-types, described below. The following table describes each built-in type in turn, giving its semantics and the corresponding Cedar type, if any. Lisp Cedar Parameter Semantics SSMALLP INTEGER small numbers in the range -215 - 215-1 FIXP INT numbers in the range -231 - 231-1 BOOLEAN BOOLEAN NIL/non-NIL <-> FALSE/TRUE STRING Rope.ROPE "random character strings" ATOM ATOM uniqueIdentifer <-> $uniqueIdentifier ARB Rope.ROPE The escape type - it uses READ and PRINT to move an arbitrary printable Lisp object (Only between Lisp and Lisp - Cedar wouldn't know what to do if it saw one) STREAM Rope.ROPE () Like a string, but goes directly into a stream, which may or may not already be open, and will be handed back open. ENUMERATION {a, b, c} (a b c) Small cardinal numbers treated as named elements. On the lisp side atoms, on the cedar side the elements of the type. BITS [0..n] (m) m bit field with a small number in it. CARDINAL is pre-declared as a synonym for (BITS 16) and LONGCARDINAL for (BITS 32). LIST LIST OF () A list of elements of type REF REF () Basically a no-op in Lisp, except wrt SEQRECORDs, q.v. RECORD RECORD (+) Cannot appear directly in an arg spec - must be declared as a type and used by name. Each field gives a name and a type. The fields must correspond to the fields of an actual Lisp record of some kind. SEQRECORD RECORD (+) As RECORD, but must have SEQUENCE as its last field. SEQUENCE SEQUENCE () Fixed length sequence of elements of type RESULT (+) Used to specify the types of compound returned values. Each field gives a name and a type. The fields must correspond to the fields of an actual Lisp record of some kind, so that e.g. an instance of of it can be created to hand over the result of a call. SIGARGS (+) As RESULT, but to deal with the fact that in Lisp signals have only one argument, which may be compound. The syntax of the spec is confused slightly by the necessity for distinguishing single from multiple return values, and atomic from compound arguments to signals. The type of such things is coded by special values in the argName field of a spec, RETURNS for the return value and ARGS for the signal arguments. These specs may either have a single typed parameter, or name a compound type of the sort RESULT or SIGARGS respectively, which in turn list the sub-components. There is a simple example supplied in the file RPC-EXAMPLE. After it is loaded, If we call (Lupine 'RPC-Example SimpleSpec SimpleSignals SimpleTypes "magic-string") we will get back the names of a pair of filecoms for all the necessary stubs etc. The magic-string will be used as a default in the process of matching server and client at the time of initial connection. If you want to allow it to default to the same value as the default generated by Cedar Lupine, you can read it out of the file SimpleRpcClientImpl.mesa, at the point in the definition of ImportInterface where it says: type: IF ~IsNull[type] THEN type ELSE "magic-string", To actually export the interface so that clients can call you, you must call e.g. ExportRPC-Example( ), where the user and password are required if the instance name you give is an rName. The user must be the owner of the rName, and the password must be either a Grapevine key, or a Lisp-internal password, as from \INTERNAL/GETPASSWORD. Likewise to import an interface exported by someone else, use e.g. ImportRPC-Example( ). See the forthcoming Cedar RPC documentation for a discussion of all the options with respect to establishing connections. In brief the argument names the service provided or required, and the argument names the machine which is (expected) to provide it. For simple applications use a mnemonic string for or let it default, and use a string or atom name for the machine. Note that on nameless machines this should be (PORTSTRING(ETHERHOSTNUMBER)). On import, =BROADCAST will find any server of the given on the local (net 0) net. The argument is always optional, defaulting to something which will match all versions. If provided it must be a CONS-pair of the form (lowest . highest), where is the lowest numbered version you support/can talk to, and the highest. A complete example is the easiest way to understand how all this works. RPC-EXAMPLE and RPC-EXAMPLEUSER are the basis for such an example. The functions in RPC-EXAMPLEUSER call the functions in RPC-EXAMPLE. RPC-EXAMPLE contains the necessary arguments for Lupine. RPC-EXAMPLECLIENT and RPC-EXAMPLESERVER contain the stubs generated by Lupine. RPC-EXAMPLECLIENT and RPC-EXAMPLEUSER would be loaded into one machine, and RPC-EXAMPLE and RPC-EXAMPLESERVER into another. RPC-EXAMPLE-CEDAR.DF is a directory of all the Cedar files needed to make up the equivalent server and/or client in Cedar. Multiple connections/Symmetric connections If you wish to communicate via a particular interface with more than one other host, you can call ImportXxx once for each host. The result of ImportXxx is a handle you can supply to subsequent calls through the interface to identify the particular server you want to address the call to: (SETQ h1 (ImportRPC-Example NIL "Host1"] (SETQ h2 (ImportRPC-Example NIL "Host2"] (Simple 3 "abc" h1) will call the server on Host1, (Simple 3 "abc" h2) will call the server on Host2. This n+1st argument defaults to the most recently imported interface for the package. Handles can be supplied to UnimportXxx to remove a host from use - if no argument is supplied all interfaces are closed. The story above about the names of stubs is slightly simplified. To protect against name conflicts and allow a single machine to be both server and client, various name changes and MOVDs are done behind the scenes. The default is that the client stub will be called e.g. "Simple", but the actually implementing function on the server side will be moved to Hidden.Simple, which is what the server stub will call. To prevent this, answer No to the query when the server stubs are loaded. In a parallel fashion, the client stubs are actually initially generated with GENSYMed names, e.g. RPCClientStub.Simple to avoid redefining the original functions, and moved into place on loading. The following examples should make this all clear: Making the stubs and running. _(LOAD 'RPC-EXAMPLE) Get the functions and the specs _(Lupine 'RPC-Example SimpleSpec SimpleSignals SimpleTypes) Create the stubs _(CLEANUP RPC-EXAMPLECLIENT RPC-EXAMPLESERVER) Save the results To test running server and client on this machine: _(HideRPC-ExampleServerMovds) Hide the server _(MovdsForRPC-Example) Install the client _(ExportRPC-Example NIL (ETHERHOSTNAME)) ThisMachine is now a server _(SETQ SomeMachineHandle (ImportRPC-Example NIL (ETHERHOSTNAME))) Starting a server from scratch _(LOAD 'RPC-EXAMPLE.DCOM) _(LOAD 'RPC-EXAMPLESERVER.DCOM) the order of loading is important Hide server fns (must have been already loaded)? Yes or no, as appropriate - if you are going to be a client as well you must say yes. _(ExportRPC-Example NIL (ETHERHOSTNAME)) This machine is now a server Starting a client from scratch (if both then this follows setup as server) _(LOAD 'RPC-EXAMPLECLIENT.DCOM) _(SETQ SomeMachineHandle (ImportRPC-Example NIL "NameOfServer")) Server here must have exported or this will time out or fail. Secure communication You can choose to have any of your remote calls encrypted for security by passing as the n+2nd argument to any call the result of calling StartConversation(caller key callee 'cbcCheck), which creates a Conversation and uses it to establish a secure channel with your server. caller and key are the sorts of things which \INTERNAL/GETPASSWORD returns, and callee is an RName for the 'owner' of the server. EVAL server There is an RPC-based EVAL server and client contained in RPCEVAL.DCOM, RPCEVALSERVER.DCOM and RPCEVALCLIENT.DCOM. To use it, follow the standard procedure, as follows. To set up a server: _(LOAD 'RPCEVAL.DCOM) _(LOAD 'RPCEVALSERVER.DCOM) _(ExportRPCEval NIL (ETHERHOSTNAME)) To set up a client : _(LOAD 'RPCEVALCLIENT.DCOM) _(SETQ handle (ImportRPCEval NIL "NameOfServer")) To invoke the remote server, call (RPCEval
handle) or (RPCApply handle) where handle defaults to the last one imported, if not supplied. 4 `À 4`À4 `À4À`À +Ø +Ø +Ø HELVETICA  HELVETICA GACHA  HELVETICA € HELVETICA  HELVETICA !*G¤  !hÖJ–M »kï.!--2¯’•Œ8HÍ7LkÙ[KW E8X f hU*!))44Ðä;.3)A#2;).1  1Š/[B- ½%2#?A0Iñzº