-- RPC: Interface between stubs and runtime.

-- [Ivy]<Birrell>RPC>RPCLupine.mesa

-- Bruce Nelson		6 Oct. 1981 9:44 am PDT (Tuesday)
-- Andrew Birrell	16-Mar-82 14:09:17

DIRECTORY
Inline		USING[ BITOR, HighHalf, LongNumber, LowHalf ],
MesaRPC		USING[ Conversation, EncryptionKey, InterfaceName,
		       maxPrincipalLength, maxShortStringLength, Principal,
		       unencrypted, VersionRange ];

RPCLupine: DEFINITIONS

IMPORTS Inline =

BEGIN

-- Definitions copied across from MesaRPC.mesa for Lupine's benefit --

maxPrincipalLength: CARDINAL = MesaRPC.maxPrincipalLength;
maxShortStringLength: CARDINAL = MesaRPC.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 MesaRPC.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[ LOOPHOLE[Inline.LongNumber[num[
 	       lowbits: Inline.BITOR[Inline.LowHalf[space+2], pktAlign] - 2,
	       highbits: 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: MesaRPC.Principal, password: MesaRPC.EncryptionKey,
		      interface: MesaRPC.InterfaceName,
		      dispatcher: Dispatcher,
		      stubProtocol: MesaRPC.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 MesaRPC.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 = LONG POINTER TO ImportInstance;

ImportInstance:	TYPE;

ImportInterface: PROC[interface: MesaRPC.InterfaceName,
		      stubProtocol: MesaRPC.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 MesaRPC.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: MesaRPC.Conversation ← MesaRPC.unencrypted];
   -- initializes the RPCPkt to specify the call.  If localConversation #
   -- MesaRPC.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 MesaRPC.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 MesaRPC.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 MesaRPC.CallFailed --

Dispatcher:	TYPE = PROC[pkt: RPCPkt,
			    callLength: DataLength, lastPkt: BOOLEAN,
			    localConversation: MesaRPC.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 MesaRPC.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.