-- Copyright (C) 1982, 1984  by Xerox Corporation. All rights reserved. 
-- MesaRPCLupine.mesa, HGM, 21-Jan-84 20:31:23

-- Interface between stubs and runtime.
-- 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];

MesaRPCLupine: 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.