-- Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved. -- RPCBinding.mesa, HGM, 21-Jan-84 20:37:10 -- Cedar 5, 21-Jan-84 20:37:07 -- NIL instance => let anybody Bind in, 30-Apr-83 21:11:00 -- Deleted hack to try Local Binding first, HGM, 12-Apr-83 18:38:50 -- Patched for NIL instance, HGM, 12-Apr-83 17:23:50 -- RPC: Binding primitives -- RPCBinding.mesa -- Andrew Birrell September 2, 1982 10:25 am DIRECTORY BodyDefs USING [Connect, maxConnectLength, maxRNameLength, Password, RName], Heap USING [systemZone], Inline USING [HighHalf, LongCOPY], NameInfoDefs USING [GetConnect], NameUpdateDefs USING [SetConnect, MakeKey], PupDefs USING [GetLocalPupAddress, GetPupAddress, PupAddress, PupNameTrouble], MesaRPC USING [ CallFailed, EncryptionKey, ExportFailed, ImportFailed, InterfaceName, matchAllVersions, Principal, VersionRange], RPCInternal USING [ExportInstance, ExportTable, ImportInstance], RPCPkt USING [ DispatcherDetails, DispatcherID, ExportHandle, Machine, noDispatcher], MesaRPCLupine USING [Call, Dispatcher, GetStubPkt, pktOverhead, StartCall, StubPkt], String USING [AppendChar, AppendNumber, AppendString], System USING [GetGreenwichMeanTime]; RPCBinding: MONITOR IMPORTS Heap, Inline, NameInfoDefs, NameUpdateDefs, PupDefs, MesaRPC, MesaRPCLupine, String, System EXPORTS MesaRPC, RPCInternal, MesaRPCLupine = BEGIN LongEquivalent: PROC [a, b: LONG STRING] RETURNS [BOOLEAN] = BEGIN IF a.length # b.length THEN RETURN[FALSE] ELSE FOR i: CARDINAL IN [0..a.length) DO ac: CHARACTER = a[i]; bc: CHARACTER = b[i]; IF ac # bc THEN IF ac IN ['a..'z] THEN {IF ac + ('A - 'a) # bc THEN RETURN[FALSE]} ELSE IF bc IN ['a..'z] AND bc + ('A - 'a) # ac THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] END; InstanceInfo: TYPE = {ok, badName, allDown, noAddress}; LocateInstance: PROC [buff: BodyDefs.RName, instance: LONG STRING] RETURNS [info: InstanceInfo, isRName: BOOLEAN, host: RPCPkt.Machine] = BEGIN connect: BodyDefs.Connect = [BodyDefs.maxConnectLength]; IF instance = NIL OR instance.length > buff.maxlength THEN RETURN[badName, , ]; String.AppendString[buff, instance]; FOR i: CARDINAL DECREASING IN [0..buff.length) DO IF buff[i] = '. THEN {isRName ← TRUE; EXIT}; REPEAT FINISHED => isRName ← FALSE; ENDLOOP; IF isRName THEN SELECT NameInfoDefs.GetConnect[buff, connect] FROM individual => NULL; notFound, group => RETURN[badName, isRName, ]; allDown => RETURN[allDown, isRName, ]; ENDCASE => ERROR ELSE String.AppendString[connect, buff]; IF connect.length = 0 THEN info ← IF isRName THEN noAddress ELSE badName ELSE BEGIN addr: PupDefs.PupAddress; info ← ok; PupDefs.GetPupAddress[ @addr, connect ! PupDefs.PupNameTrouble => IF code = errorFromServer THEN { info ← IF isRName THEN noAddress ELSE badName; CONTINUE} ELSE {info ← allDown; CONTINUE}]; host ← [net: addr.net, host: addr.host]; END; END; -- ******** Binding primitives ******** -- ExportHandle: PUBLIC TYPE = RPCPkt.ExportHandle; exportTable: PUBLIC LONG POINTER TO RPCInternal.ExportTable ← Heap.systemZone.NEW[RPCInternal .ExportTable[20] ← [used: 0, entries:]]; -- Representation of exports in the GV database: -- Each interface type is a group, such as "Alpine.pa" -- Members of the group are the interface instances. -- Each interface instance is an individual, such as "MontBlanc.pa" -- Connect-site for the individual contains the exporting host. -- The syntax of instances' connect-sites is (all octal): -- net#host#mds. MakeKey: PUBLIC PROC [text: LONG STRING] RETURNS [MesaRPC.EncryptionKey] = BEGIN RETURN[NameUpdateDefs.MakeKey[text]] END; ExportInterface: PUBLIC PROC [ user: MesaRPC.Principal, password: MesaRPC.EncryptionKey, interface: MesaRPC.InterfaceName, dispatcher: MesaRPCLupine.Dispatcher, stubProtocol: MesaRPC.VersionRange, localOnly: BOOLEAN ← FALSE] RETURNS [instance: ExportHandle] = BEGIN IF interface.type = NIL THEN ERROR MesaRPC.ExportFailed[badType]; IF interface.version # MesaRPC.matchAllVersions AND interface.version.first > interface.version.last THEN ERROR MesaRPC.ExportFailed[badVersion]; instance ← AddToExportTable[interface, dispatcher, stubProtocol]; IF NOT localOnly AND interface.instance # NIL THEN BEGIN ENABLE UNWIND => [] ← UnexportInterface[instance]; buff: BodyDefs.RName = [BodyDefs.maxRNameLength]; prevHost: RPCPkt.Machine; prevInfo: InstanceInfo; isRName: BOOLEAN; [prevInfo, isRName, prevHost] ← LocateInstance[buff, interface.instance]; SELECT prevInfo FROM ok => IF prevHost = myMachine THEN RETURN; badName => ERROR MesaRPC.ExportFailed[badInstance]; allDown => ERROR MesaRPC.ExportFailed[communications]; noAddress => NULL; ENDCASE => ERROR; IF isRName THEN BEGIN connect: BodyDefs.Connect = [BodyDefs.maxConnectLength]; userBuff: BodyDefs.RName = [BodyDefs.maxRNameLength]; String.AppendNumber[connect, myMachine.net, 8]; String.AppendChar[connect, '#]; String.AppendNumber[connect, myMachine.host, 8]; String.AppendChar[connect, '#]; IF user.length > BodyDefs.maxRNameLength THEN ERROR MesaRPC.ExportFailed[badCredentials]; String.AppendString[userBuff, user]; SELECT NameUpdateDefs.SetConnect[ userBuff, LOOPHOLE[password], buff, connect] FROM individual => NULL; badPwd => ERROR MesaRPC.ExportFailed[badCredentials]; group, notFound => ERROR MesaRPC.ExportFailed[badInstance]; allDown => ERROR MesaRPC.ExportFailed[communications]; ENDCASE => ERROR; END ELSE ERROR MesaRPC.ExportFailed[badInstance]; END; END; lastExportID: RPCPkt.DispatcherID ← System.GetGreenwichMeanTime[]; -- UID on this machine -- AddToExportTable: ENTRY PROC [ interface: MesaRPC.InterfaceName, dispatcher: MesaRPCLupine.Dispatcher, stubProtocol: MesaRPC.VersionRange] RETURNS [instance: ExportHandle] = BEGIN myMDS: CARDINAL = Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]]; FOR instance IN [0..exportTable.used) DO IF exportTable[instance].id = RPCPkt.noDispatcher THEN EXIT; REPEAT FINISHED => IF exportTable.used = exportTable.length THEN RETURN WITH ERROR MesaRPC.ExportFailed[tooMany] ELSE { instance ← exportTable.used; exportTable.used ← exportTable.used + 1} ENDLOOP; exportTable[instance] ← [ lastExportID ← lastExportID + 1, dispatcher, myMDS, [ type: IF interface.type = NIL THEN NIL ELSE Heap.systemZone.NEW[StringBody [interface.type.length]], instance: IF interface.instance = NIL THEN NIL ELSE Heap.systemZone.NEW[StringBody [interface.instance.length]], version: interface.version], stubProtocol]; IF interface.type # NIL THEN Inline.LongCOPY[ from: interface.type, to: exportTable[instance].name.type, nwords: SIZE[StringBody [interface.type.length]]]; IF interface.instance # NIL THEN Inline.LongCOPY[ from: interface.instance, to: exportTable[instance].name.instance, nwords: SIZE[StringBody [interface.instance.length]]]; END; UnexportInterface: PUBLIC ENTRY PROC [instance: ExportHandle] RETURNS [ExportHandle] = { IF exportTable[instance].id # RPCPkt.noDispatcher AND instance # binderHint THEN BEGIN IF exportTable[instance].name.instance # NIL THEN Heap.systemZone.FREE[@exportTable[instance].name.instance]; IF exportTable[instance].name.type # NIL THEN Heap.systemZone.FREE[@exportTable[instance].name.type]; exportTable[instance] ← [RPCPkt.noDispatcher, NIL, 0, [NIL, NIL, ], ]; END; RETURN[instance]}; -- Details of the exported dispatcher are kept only in the exporting machine. -- The importer obtains the details by an RPC call to dispatcher 1 on that machine. -- "Bind" makes the call, "Binder" is dispatcher 1 on every machine. -- "LocalBind" accepts the call. -- "Bind" returns RPCPkt.noDispatcher for unbound instances. binderID: RPCPkt.DispatcherID = SUCC[RPCPkt.noDispatcher]; binderHint: ExportHandle = 0; binderProc: CARDINAL = 0; BinderResult: TYPE = MACHINE DEPENDENT RECORD [ stubProtocol: MesaRPC.VersionRange, version: MesaRPC.VersionRange, dispatcher: RPCPkt.DispatcherDetails]; BinderArgs: TYPE = MACHINE DEPENDENT RECORD [ request(0): CARDINAL, type(1): CARDINAL, -- offset in pkt, 0 => NIL -- instance(2): CARDINAL -- offset in pkt, 0 => NIL -- -- followed by the StringBody values for type, instance -- ]; -- Server-stub for binding calls -- Binder: MesaRPCLupine.Dispatcher = BEGIN PktString: PROC [n: CARDINAL] RETURNS [LONG STRING] = { RETURN[ IF n + SIZE[StringBody [0]] NOT IN [SIZE[BinderArgs] + SIZE[StringBody [0]]..callLength] THEN NIL ELSE LOOPHOLE[@pkt[n]]]}; args: LONG POINTER TO BinderArgs = LOOPHOLE[@pkt.data]; result: LONG POINTER TO BinderResult = LOOPHOLE[@pkt.data]; SELECT args.request FROM binderProc => result↑ ← LocalBind[PktString[args.type], PktString[args.instance]]; ENDCASE => NULL -- ??-- ; RETURN[SIZE[BinderResult]]; END; -- Server-implementation for binding calls -- LocalBind: ENTRY PROC [type, instance: LONG STRING] RETURNS [BinderResult] = BEGIN FOR i: CARDINAL IN [1..exportTable.used) DO IF exportTable[i].id # RPCPkt.noDispatcher AND (type = NIL OR LongEquivalent[type, exportTable[i].name.type]) AND (instance = NIL OR exportTable[i].name.instance = NIL OR LongEquivalent[instance, exportTable[i].name.instance]) THEN RETURN[ [ exportTable[i].stubProtocol, exportTable[i].name.version, [ exportTable[i].mds, exportTable[i].id, i]]]; ENDLOOP; RETURN[[, , [, RPCPkt.noDispatcher, ]]] END; -- user-stub for binding calls -- RemoteBind: PROC [host: RPCPkt.Machine, type, instance: LONG STRING] RETURNS [BinderResult] = BEGIN binderInterface: ImportInstance ← [host, [, binderID, binderHint]]; argSize: CARDINAL = SIZE[BinderArgs]; pktSize: CARDINAL = MAX[ argSize + 2*SIZE[StringBody [BodyDefs.maxRNameLength]], SIZE[BinderResult]]; pktData: ARRAY [0..MesaRPCLupine.pktOverhead + pktSize) OF WORD; pkt: MesaRPCLupine.StubPkt = MesaRPCLupine.GetStubPkt[@pktData]; args: POINTER TO BinderArgs = LOOPHOLE[@pkt.data]; resultLength: CARDINAL; MesaRPCLupine.StartCall[pkt, @binderInterface]; BEGIN used: CARDINAL; args↑ ← [binderProc, 0, 0]; used ← SIZE[BinderArgs]; IF type # NIL THEN BEGIN args.type ← used; Inline.LongCOPY[ from: type, to: @pkt[used], nwords: SIZE[StringBody [type.length]]]; used ← used + SIZE[StringBody [type.length]]; END; IF instance # NIL THEN BEGIN args.instance ← used; Inline.LongCOPY[ from: instance, to: @pkt[used], nwords: SIZE[StringBody [instance.length]]]; used ← used + SIZE[StringBody [instance.length]]; END; IF used > pktSize THEN ERROR; [resultLength, ] ← MesaRPCLupine.Call[pkt, used, pktSize]; END; RETURN[LOOPHOLE[@pkt.data, POINTER TO BinderResult]↑]; END; ImportInstance: PUBLIC TYPE = RPCInternal.ImportInstance; LocateImportInstance: PROC [instance: LONG STRING] RETURNS [host: RPCPkt.Machine] = BEGIN buff: BodyDefs.RName = [BodyDefs.maxRNameLength]; info: InstanceInfo; isRName: BOOLEAN; [info, isRName, host] ← LocateInstance[buff, instance]; SELECT info FROM ok => NULL; badName => ERROR MesaRPC.ImportFailed[badInstance]; allDown => ERROR MesaRPC.ImportFailed[communications]; noAddress => ERROR MesaRPC.ImportFailed[unbound]; ENDCASE => ERROR; END; ImportInterface: PUBLIC PROC [ interface: MesaRPC.InterfaceName, stubProtocol: MesaRPC.VersionRange, localOnly: BOOLEAN ← FALSE] RETURNS [handle: LONG POINTER TO ImportInstance] = BEGIN host: RPCPkt.Machine; dispatcher: RPCPkt.DispatcherDetails; IF interface.type = NIL THEN ERROR MesaRPC.ExportFailed[badType]; host ← LocateImportInstance[interface.instance]; dispatcher ← TryBinding[ host, interface, stubProtocol]; RETURN[Heap.systemZone.NEW[ImportInstance ← [host, dispatcher]]] END; TryBinding: PROC [ host: RPCPkt.Machine, impName: MesaRPC.InterfaceName, stubProtocol: MesaRPC.VersionRange] RETURNS [RPCPkt.DispatcherDetails] = BEGIN expDetails: BinderResult; expDetails ← RemoteBind[ host, impName.type, impName.instance ! MesaRPC.CallFailed => IF why = busy THEN RETRY ELSE ERROR MesaRPC.ImportFailed[communications]]; IF expDetails.dispatcher.dispatcherID = RPCPkt.noDispatcher THEN ERROR MesaRPC.ImportFailed[unbound]; IF stubProtocol # MesaRPC.matchAllVersions AND expDetails.stubProtocol # MesaRPC.matchAllVersions AND (stubProtocol.first > expDetails.stubProtocol.last OR stubProtocol.last < expDetails.stubProtocol.first) THEN ERROR MesaRPC.ImportFailed[stubProtocol]; IF impName.version # MesaRPC.matchAllVersions AND impName.version.first > impName.version.last THEN ERROR MesaRPC.ImportFailed[badVersion]; IF impName.version # MesaRPC.matchAllVersions AND expDetails.version # MesaRPC.matchAllVersions AND (impName.version.first > expDetails.version.last OR impName.version.last < expDetails.version.first) THEN ERROR MesaRPC.ImportFailed[wrongVersion]; RETURN[expDetails.dispatcher]; END; UnimportInterface: PUBLIC PROC [handle: LONG POINTER TO ImportInstance] RETURNS [LONG POINTER TO ImportInstance] = { Heap.systemZone.FREE[@handle]; RETURN[NIL]}; myMachine: RPCPkt.Machine; -- ******** Initialization ******** -- Initialize: ENTRY PROC = BEGIN BEGIN myAddr: PupDefs.PupAddress = PupDefs.GetLocalPupAddress[[0, 0], NIL]; myMachine ← [net: myAddr.net, host: myAddr.host]; END; IF exportTable.used = 0 THEN BEGIN binderMDS: CARDINAL = Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]]; exportTable[binderHint] ← [ binderID, Binder, binderMDS, ["Binder", NIL, [0, 0]], [0, 0]]; exportTable.used ← 1; END; END; Initialize[]; END.