DIRECTORY PrincOpsUtils USING[ BITOR, HighHalf, LowHalf, MakeLongPointer ], RPC USING[ Conversation, EncryptionKey, InterfaceName, maxPrincipalLength, maxShortStringLength, Principal, unencrypted, VersionRange ]; RPCLupine: DEFINITIONS IMPORTS PrincOpsUtils = BEGIN -- Definitions copied across from RPC.mesa for Lupine's benefit maxPrincipalLength: CARDINAL = RPC.maxPrincipalLength; maxShortStringLength: CARDINAL = RPC.maxShortStringLength; Encapsulation: PRIVATE TYPE[7]; -- large enough for all current networks Header: PRIVATE TYPE[10+6+4]; maxPupWords: PRIVATE CARDINAL = 266 -- Pup data -- + 10 -- Pup header -- + 1 -- Pup checksum --; encryptionBlockSize: PRIVATE CARDINAL = 4; SecurityChecksum: PRIVATE TYPE[2]; maxDataLength: CARDINAL = ( ( 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 { RETURN[ LOOPHOLE[PrincOpsUtils.BITOR[space+2, pktAlign],POINTER]-2 ] }; GetRPCPkt: PROC[ space: LONG POINTER TO UNSPECIFIED] RETURNS[--pkt:-- RPCPkt ] = INLINE { RETURN[ PrincOpsUtils.MakeLongPointer[ low: LOOPHOLE[PrincOpsUtils.BITOR[PrincOpsUtils.LowHalf[space+2], pktAlign] - 2], high: PrincOpsUtils.HighHalf[space+2] ]] }; -- Binding primitives for use by stubs -- 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 ]; UnexportInterface: PROC[ interfaceInstance: ExportHandle ] RETURNS[ExportHandle]; ImportHandle: TYPE = REF ImportInstance; ImportInstance: TYPE; ImportInterface: PROC[interface: RPC.InterfaceName, stubProtocol: RPC.VersionRange, localOnly: BOOLEAN _ FALSE] RETURNS[ interfaceInstance: ImportHandle ]; UnimportInterface: PROC[ interfaceInstance: ImportHandle ] RETURNS[ ImportHandle ]; StartCall: PROC[callPkt: RPCPkt, interface: ImportHandle, localConversation: RPC.Conversation _ RPC.unencrypted]; Call: PROC[pkt: RPCPkt, callLength: DataLength, maxReturnLength: DataLength, signalHandler: Dispatcher _ NIL] RETURNS[ returnLength: DataLength, lastPkt: BOOLEAN]; SendPrelimPkt: PROC[pkt: RPCPkt, length: DataLength]; ReceiveExtraPkt: PROC[prevPkt: RPCPkt] RETURNS[ length: DataLength, lastPkt: BOOLEAN]; Dispatcher: TYPE = PROC[pkt: RPCPkt, callLength: DataLength, lastPkt: BOOLEAN, localConversation: RPC.Conversation ] RETURNS[ returnLength: DataLength ]; StartSignal: PROC[signalPkt: RPCPkt]; RejectUnbound: ERROR; RejectProtocol: ERROR; END. RPC: Interface between stubs and runtime. [Ivy]RPC>RPCLupine.mesa Bruce Nelson 6 Oct. 1981 9:44 am PDT (Tuesday) Andrew Birrell 16-Mar-82 14:09:17 Last Edited by: Birrell, September 7, 1983 3:28 pm 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]; PUP header + RPC protocol overhead + dispatcher description. Must be aligned to quad-word + 2 -- maximum value of length field of Pup packet, in words, as defined in [Maxc]PupSpec.press -- Maximum data words in RPCPkt or StubPkt. The following calculation assumes that the encrypted part of a header is a multiple of encryptionBlockSize -- 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. 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! 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. 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 -- Removes export from local dispatcher table and binding database. Returns an ExportHandle that will fault if used. -- 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 -- 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. initializes the RPCPkt to specify the call. If localConversation # RPC.unencrypted then the call will be made securely. 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 -- 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 Waits for next pkt. Used for receiving non-first packets of arguments or results. Assumes maxlength = maxDataLength. May raise RPC.CallFailed -- "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 -- 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"! 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. 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. ÊU˜Jšœ)™)Jšœ ™ Jšœ/™/Jšœ"™"J™2šÏk ˜ Jšœœœ'˜AJšœœ˜ˆJ˜—šœ ˜Jšœ˜—J˜Jš˜J˜JšÏc?˜?J˜Jšœœ˜6Jšœœ˜:J˜J˜J˜šœ²™²J™Jšœ3™3Jšœ8™8—J™šœ›™›J™JšœG™GJšœ8™8—J˜šœœœ˜Jšž(˜(J˜—šœ œœ ˜Jšœ=ž%™bJ˜—šœ œœ˜Jšœ6ž*™`Jšœž˜Jšœž˜Jšœžœ˜J˜—Jšœœœ˜*J˜Jšœœœ˜"J˜šœœ˜Jšœož(™—˜Jšœž˜Jšœœ ˜Jšœžœ˜%—J˜Jšœœ˜J˜—Jšœ œ˜&J˜šœ œ ž%˜FJšœœ˜Jšœž˜&Jšœœ˜Jšœžœ˜J˜—šœ œœ˜Jšž*˜*J˜—Jšœ œœœ ˜'J˜Jšœ œœ ˜J˜š œ œœ œœ˜-Jšž˜Jšœ œ˜%Jšœœ˜%Jšœœ ž˜,Jš œœœ œ œ˜3J˜—š Ïn œœ œœ œ˜0Jšœžœ ˜#Jšœ4žC™wJšœ™Jšœ™Jšœ™Jšœ™Jš œœœœœ˜IJ˜—š Ÿ œœ œœœ œ˜4Jšœžœ ˜"šœ4žCœ™}Jšœ™Jšœ™Jšœ™Jšœ™—Jšœ?™?šœœ ˜(Jšœœœ0˜Q˜%J˜J˜J˜J˜J˜J˜———Jšž)˜)J˜Jšœê™êJ˜Jšœò™òJ˜Jšœa™aJ˜šœœœœ˜#Jšž*˜*J˜—šŸœœ2˜GJ˜J˜J˜Jšœ œœ˜Jšœ$˜+JšœÃž,™ïJ˜—šŸœœ#˜:Jšœ˜JšœHž,™tJ˜—Jšœœœ˜(J˜Jšœœ˜J˜šŸœœ˜3J˜Jšœ œœ˜Jšœ$˜+Jšœ{ž9™´J˜—šŸœœ#˜:Jšœ˜JšœBž™^J˜J˜J˜—Jšœ%™%J˜šœ4™4Jšœ1™1—šœ3™3Jšœ ™ —J˜Jšœ×™×J˜Jšœð™ðJ˜šŸ œœ˜ J˜J˜7JšœCž5™xJ˜—šŸœœ%˜/J˜Jšœœ˜ šœ˜"Jšœ œ˜—Jšœ8™8Jšœ:ž1™kJ˜—šŸ œœ"˜5Jšœ•™•J˜—šŸœœ˜&šœ˜Jšœ œ˜—JšœRžA™“J˜—šŸ œœœ ˜$Jšœ!œ˜)J˜%Jšœ˜$JšœÅž;™€J˜—šŸ œœ˜%šœ[™[Jšœ&™&—JšœÍ™ÍJ˜—šœœ˜Jšœ ™ —J˜šœœ˜Jšœ¦™¦J˜—Jšœ˜J˜—…— &*—