RPC: Pilot compatible interface between stubs and runtime.
MesaRPCLupine.mesa
Last Edited by: Andrew Birrell, September 13, 1983 1:38 pm
DIRECTORY
PrincOpsUtils--Inline-- USING[ BITOR, HighHalf, LowHalf, MakeLongPointer ],
MesaRPC USING[ Conversation, EncryptionKey, InterfaceName, maxPrincipalLength, maxShortStringLength, Principal, unencrypted, VersionRange ];
MesaRPCLupine:
DEFINITIONS
IMPORTS PrincOpsUtils =
BEGIN
OPEN RPC: MesaRPC, Inline: PrincOpsUtils;
-- Definitions copied across from RPC.mesa for Lupine's benefit
maxPrincipalLength: CARDINAL = RPC.maxPrincipalLength;
maxShortStringLength: CARDINAL = RPC.maxShortStringLength;
StubPkt's are allocated in user-stubs. RPCPkt's are either StubPkt's or packets allocated by the RPC server processes. StubPkt's and RPCPkt's are never seen by the vanilla ethernet driver. StubPktData storage must be aligned to allow the packets to be passed to the ethernet face, and we must correct for the fact that Pup checksums are at the end of the packet. To allocate a packet having up to "n" words of data, declare an array of n+pktOverhead words, then call "GetStubPkt" to obtain the aligned packet. Thus, to allocate space for 250 words, declare:
space: ARRAY[1..RPCLupine.pktOverhead+250] OF WORD;
pkt: RPCLupine.StubPkt = RPCLupine.GetStubPkt[@space];
Horrible! It could be much neater if the compiler supported declaring sequence variables in the local frame. To allocate a maximum size StubPkt, declare:
space: ARRAY[1..RPCLupine.pktOverhead+RPCLupine.maxDataLength] OF WORD;
pkt: RPCLupine.StubPkt = RPCLupine.GetStubPkt[@space];
Encapsulation:
PRIVATE
TYPE[7];
-- large enough for all current networks
Header:
PRIVATE
TYPE[10+6+4];
PUP header + RPC protocol overhead + dispatcher description. Must be aligned to quad-word + 2 --
maxPupWords:
PRIVATE
CARDINAL =
maximum value of length field of Pup packet, in words, as defined in [Maxc]<Pup>PupSpec.press --
266 -- Pup data --
+ 10 -- Pup header --
+ 1 -- Pup checksum --;
encryptionBlockSize: PRIVATE CARDINAL = 4;
SecurityChecksum: PRIVATE TYPE[2];
maxDataLength:
CARDINAL =
Maximum data words in RPCPkt or StubPkt. The following calculation assumes that the encrypted part of a header is a multiple of encryptionBlockSize --
( ( maxPupWords
- 1 -- Pup checksum --
- SIZE[Header] )
/ encryptionBlockSize --round down--)
* encryptionBlockSize
- SIZE[SecurityChecksum];
DataLength: TYPE = [0..maxDataLength];
pktOverhead:
CARDINAL = pktAlign
-- wastage for aligning the buffer --
+ SIZE[StubPktData[0]]
+ encryptionBlockSize-1 -- rounding --
+ SIZE[SecurityChecksum]
+ 1 -- Pup checksum --;
pktAlign:
PRIVATE
WORD = 3B;
-- maximum wastage for quad-word alignment
StubPkt: TYPE = POINTER TO StubPktData;
RPCPkt: TYPE = LONG StubPkt;
StubPktData:
TYPE =
MACHINE
DEPENDENT
RECORD[
-- must be suitably aligned
convHandle: PRIVATE RPC.Conversation,
encapsulation: PRIVATE Encapsulation,
header: PRIVATE Header, -- Pup/OIS header --
data: SEQUENCE COMPUTED DataLength OF UNSPECIFIED];
GetStubPkt:
PROC[ space:
POINTER
TO
UNSPECIFIED]
RETURNS[--pkt:-- StubPkt ] = INLINE
returns pointer aligned to quad-word plus one. That causes Ethernet-one encapsulation to start at quad-word (honest!).
i.e. space=0 => result=1,
space=1 => result=1,
space=2 => result=5,
space=3 => result=5.
{ RETURN[ LOOPHOLE[Inline.BITOR[space+2, pktAlign],POINTER]-2 ] };
GetRPCPkt:
PROC[ space:
LONG
POINTER
TO
UNSPECIFIED]
RETURNS[--pkt:-- RPCPkt ] = INLINE
returns pointer aligned to quad-word plus one. That causes Ethernet-one encapsulation to start at quad-word (honest!). I.e.
space=0 => result=1,
space=1 => result=1,
space=2 => result=5,
space=3 => result=5.
Unfortunately, the alignment may take us across a 64K boundary!
{
RETURN[ Inline.MakeLongPointer[
low: LOOPHOLE[Inline.BITOR[Inline.LowHalf[space+2], pktAlign] - 2],
high: Inline.HighHalf[space+2]
]] };
-- Binding primitives for use by stubs --
Exporter specifies which interface type is being exported, and which instance of that type, and which "version" of that type. The stub should default the interface type to the timestamp of the definitions file bcd. The interface instance defaults to a newly created UID. The version is client-specified, meaning which range of versions this exporter supports.
Importer specifies required interface type and instance, and version range. The stub should default the interface type to the timestamp of the definitions file bcd. Interface instance defaults to "any". The version range defaults to "any".
The stubProtocol in imports and exports is used to check compatability of stub protocol versions.
ExportHandle:
TYPE[
SIZE[
CARDINAL]];
-- index to table of dispatcher procedures
ExportInterface:
PROC[user: RPC.Principal, password: RPC.EncryptionKey,
interface: RPC.InterfaceName,
dispatcher: Dispatcher,
stubProtocol: RPC.VersionRange,
localOnly: BOOLEAN ← FALSE]
RETURNS[ interfaceInstance: ExportHandle ];
Registers export in binding database and local dispatcher table. The user and password provide credentials to update the binding database. If localOnly=TRUE then the network binding database is not updated. May raise RPC.ExportFailed --
UnexportInterface:
PROC[ interfaceInstance: ExportHandle ]
RETURNS[ExportHandle];
Removes export from local dispatcher table and binding database. Returns an ExportHandle that will fault if used. --
ImportHandle: TYPE = REF ImportInstance;
ImportInstance: TYPE;
ImportInterface:
PROC[interface: RPC.InterfaceName,
stubProtocol: RPC.VersionRange,
localOnly: BOOLEAN ← FALSE]
RETURNS[ interfaceInstance: ImportHandle ];
Searches binding database for exporter of specified interface. If localOnly=TRUE then the import will succeed only if the exporter is on this host. May raise RPC.ImportFailed --
UnimportInterface:
PROC[ interfaceInstance: ImportHandle ]
RETURNS[ ImportHandle ];
Releases local representation of binding. Returns an ImportHandle that will fault if used. --
Stubs: sending and receiving packets.
For a user-stub making a call, the call sequence is:
StartCall, SendPrelimPkt*, Call, ReceiveExtraPkt*
When a Dispatcher is invoked, its call sequence is:
ReceiveExtraPkt*, SendPrelimPkt*
The RPC runtime assumes that within one of these sequences, the same stub packet is being used. All state associated with the call is maintained in the packet buffers. StartCall and StartReturn perform no communication, and raise no signals. The other procedures can each raise RPCSignals.CallFailed. All may safely be aborted, if desired.
The RPC runtime transports arguments and results from a user-stub to the appropriate dispatcher. Presumably, the first argument packet will specify to the dispatcher which client procedure to call; this is of no concern to the RPC runtime.
StartCall:
PROC[callPkt: RPCPkt,
interface: ImportHandle,
localConversation: RPC.Conversation ← RPC.unencrypted];
initializes the RPCPkt to specify the call. If localConversation # RPC.unencrypted then the call will be made securely.
Call:
PROC[pkt: RPCPkt, callLength: DataLength,
maxReturnLength: DataLength,
signalHandler: Dispatcher ← NIL]
RETURNS[ returnLength: DataLength,
lastPkt: BOOLEAN];
Sends the packet and waits for first return packet. The
signalHandler gets invoked if a signal is passed back from the callee machine. May raise RPC.CallFailed --
SendPrelimPkt:
PROC[pkt: RPCPkt, length: DataLength];
Sends the packet and waits for ack. Packet may then be re-used Used for sending non-last packets of arguments or results. May raise RPC.CallFailed
ReceiveExtraPkt:
PROC[prevPkt: RPCPkt]
RETURNS[ length: DataLength,
lastPkt: BOOLEAN];
Waits for next pkt. Used for receiving non-first packets of arguments or results. Assumes maxlength = maxDataLength. May raise RPC.CallFailed --
Dispatcher:
TYPE =
PROC[pkt: RPCPkt,
callLength: DataLength, lastPkt: BOOLEAN,
localConversation: RPC.Conversation ]
RETURNS[ returnLength: DataLength ];
"pkt" is the first argument packet. This must be passed to any calls of ReceiveExtraPkt or SendPrelimPkt; the stub leaves the last return pkt data in "pkt". "pkt" has maxlength = maxDataLength. The caller of the dispatcher will catch RPC.CallFailed --
StartSignal:
PROC[signalPkt: RPCPkt];
Called to indicate that a signal is about to be raised in the caller. The stub then calls:
SendPrelimPkt*, Call, ReceiveExtraPkt*
then RESUME's the signal with the appropriate results. On the caller machine, this looks a call which invokes the signalHandler for his call of "Call". If the signal gets unwound on the caller machine, the call of "Call" gets unwound without the stub being told (except by the "UNWIND" signal). The catch-phrases on the caller machine may of course raise another signal, which comes back to this machine as an invokation of the signalHandler passed to "Call"!
RejectUnbound:
ERROR;
May be raised by a dispatcher to cause the remote caller to get CallFailed[unbound]. The dispatcher must have sent no result packets before raising this error.
RejectProtocol:
ERROR;
May be raised by a dispatcher to cause the remote caller to get CallFailed[protocolError]. The dispatcher must have sent no result packets before raising this error.
END.