RPC: Binding primitives
RPCBinding.mesa
Andrew Birrell September 7, 1983 4:40 pm
Last Edited by: MBrown, March 30, 1983 4:20 pm
DIRECTORY
BasicTime USING[ earliestGMT, Now, Period ],
Convert USING[ RopeFromInt ],
GVBasics USING[ Connect, MakeKey, maxRNameLength, Password, RName ],
GVNames USING[ ConnectInfo, GetConnect, SetConnect ],
PrincOpsUtils USING[ HighHalf, LongCOPY ],
PupDefs USING[ AnyLocalPupAddress, GetPupAddress, PupAddress, PupNameTrouble ],
Rope USING[ Cat, ROPE, Fetch, Find, Flatten, Length, Text ],
RPC USING[ CallFailed, EncryptionKey, ExportFailed, ImportFailed, InterfaceName, matchAllVersions, Principal, VersionRange ],
RPCInternal USING[ ExportInstance, ExportTable, ImportInstance ],
RPCPkt USING[ DispatcherDetails, DispatcherID, ExportHandle, Machine, noDispatcher],
RPCLupine USING[ Call, Dispatcher, GetStubPkt, pktOverhead, StartCall, StubPkt];
RPCBinding:
MONITOR
IMPORTS BasicTime, Convert, GVBasics, GVNames, PrincOpsUtils, PupDefs, Rope, RPC, RPCLupine
EXPORTS RPC, RPCInternal, RPCLupine =
BEGIN
LongEquivalent:
PROC[a:
LONG
STRING, b: Rope.
ROPE]
RETURNS[
BOOLEAN] =
BEGIN
IF a.length # b.Length[]
THEN RETURN[FALSE]
ELSE
FOR i:
CARDINAL
IN [0..a.length)
DO ac:
CHAR = a[i];
bc: CHAR = b.Fetch[i];
IF ac # bc
THEN
BEGIN
au: CHAR = IF ac IN ['a..'z] THEN 'A + (ac-'a) ELSE ac;
bu: CHAR = IF bc IN ['a..'z] THEN 'A + (bc-'a) ELSE bc;
IF au # bu THEN RETURN[FALSE];
END
ENDLOOP;
RETURN[TRUE]
END;
CopyRope:
PROC[from: Rope.
ROPE, to:
LONG
POINTER] =
BEGIN
flat: Rope.Text = Rope.Flatten[from];
text: LONG POINTER TO TEXT = LOOPHOLE[flat];
PrincOpsUtils.LongCOPY[from: LOOPHOLE[text], to: to, nwords: SIZE[StringBody[text.length]]];
END;
InstanceInfo: TYPE = { ok, badName, allDown, noAddress };
LocateInstance:
PROC[instance: Rope.
ROPE]
RETURNS[info: InstanceInfo, isRName: BOOLEAN, host: RPCPkt.Machine] =
BEGIN
connect: Rope.ROPE ← NIL;
IF instance.Length[] > GVBasics.maxRNameLength THEN RETURN[badName,,];
isRName ← Rope.Find[instance, "."] >= 0;
IF isRName
THEN
BEGIN
info: GVNames.ConnectInfo;
[info, connect] ← GVNames.GetConnect[instance];
SELECT info
FROM
individual => NULL;
notFound, group => RETURN[badName,isRName,];
allDown => RETURN[allDown,isRName,];
ENDCASE => ERROR;
END
ELSE connect ← instance;
IF connect.Length[] = 0
THEN info ← IF isRName THEN noAddress ELSE badName
ELSE
BEGIN
addr: PupDefs.PupAddress;
info ← ok;
addr ← PupDefs.GetPupAddress[[0,0], 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
REF RPCInternal.ExportTable ←
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
SAFE
PROC[text: Rope.
ROPE]
RETURNS[
RPC.EncryptionKey ] =
CHECKED
BEGIN
RETURN[GVBasics.MakeKey[text]]
END;
ExportInterface:
PUBLIC
PROC[user: RPC.Principal,
password: RPC.EncryptionKey,
interface: RPC.InterfaceName,
dispatcher: RPCLupine.Dispatcher,
stubProtocol: RPC.VersionRange,
localOnly: BOOLEAN ← FALSE]
RETURNS[ instance: ExportHandle ] =
BEGIN
IF interface.type = NIL THEN ERROR RPC.ExportFailed[badType];
IF interface.version # RPC.matchAllVersions
AND interface.version.first > interface.version.last
THEN ERROR RPC.ExportFailed[badVersion];
instance ← AddToExportTable[interface, dispatcher, stubProtocol];
IF NOT localOnly
THEN
BEGIN
ENABLE UNWIND => [] ← UnexportInterface[instance];
prevHost: RPCPkt.Machine;
prevInfo: InstanceInfo;
isRName: BOOLEAN;
[prevInfo, isRName, prevHost] ← LocateInstance[interface.instance];
SELECT prevInfo
FROM
ok => IF prevHost = myMachine THEN RETURN;
badName => ERROR RPC.ExportFailed[badInstance];
allDown => ERROR RPC.ExportFailed[communications];
noAddress => NULL;
ENDCASE => ERROR;
IF isRName
THEN
BEGIN
IF user.Length[] > GVBasics.maxRNameLength
THEN ERROR RPC.ExportFailed[badCredentials];
SELECT GVNames.SetConnect[user,
LOOPHOLE[password],
interface.instance,
Rope.Cat[
Convert.RopeFromInt[myMachine.net, 8, FALSE],
"#",
Convert.RopeFromInt[myMachine.host, 8, FALSE],
"#"]
] FROM
individual, noChange => NULL;
group, notFound => ERROR RPC.ExportFailed[badInstance];
badPwd, notAllowed => ERROR RPC.ExportFailed[badCredentials];
allDown => ERROR RPC.ExportFailed[communications];
ENDCASE => ERROR;
END
ELSE ERROR RPC.ExportFailed[badInstance];
END;
END;
lastExportID: RPCPkt.DispatcherID ←
BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]];
-- UID on this machine
AddToExportTable:
ENTRY
PROC[interface: RPC.InterfaceName,
dispatcher: RPCLupine.Dispatcher,
stubProtocol: RPC.VersionRange]
RETURNS[ instance: ExportHandle ] =
BEGIN
myMDS: CARDINAL = PrincOpsUtils.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 RPC.ExportFailed[tooMany]
ELSE { instance ← exportTable.used; exportTable.used ← exportTable.used+1 }
ENDLOOP;
exportTable[instance] ←
[lastExportID ← lastExportID+1,
dispatcher,
myMDS,
interface,
stubProtocol];
END;
UnexportInterface:
PUBLIC
ENTRY
PROC[ instance: ExportHandle ]
RETURNS[ExportHandle] =
{
IF exportTable[instance].id # RPCPkt.noDispatcher
AND instance # binderHint
THEN exportTable[instance] ← [RPCPkt.noDispatcher,NIL,0,[NIL,NIL,],];
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: RPC.VersionRange,
version: RPC.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: RPCLupine.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 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:
SAFE
PROC[host: RPCPkt.Machine,
type, instance: Rope.ROPE]
RETURNS[BinderResult] = TRUSTED
BEGIN
binderInterface: REF ImportInstance = NEW[ImportInstance ← [host,[,binderID,binderHint]]];
argSize: CARDINAL = SIZE[BinderArgs];
pktSize:
CARDINAL =
MAX[argSize+2*
SIZE[StringBody[GVBasics.maxRNameLength]],
SIZE[BinderResult] ];
pktData: ARRAY [0..RPCLupine.pktOverhead+pktSize) OF WORD;
pkt: RPCLupine.StubPkt = RPCLupine.GetStubPkt[@pktData];
args: POINTER TO BinderArgs = LOOPHOLE[@pkt.data];
resultLength: CARDINAL;
IF type.Length[] > GVBasics.maxRNameLength
OR instance.Length[] > GVBasics.maxRNameLength
THEN ERROR RPC.ImportFailed[unbound];
RPCLupine.StartCall[pkt, binderInterface];
BEGIN
used: CARDINAL;
args^ ← [binderProc, 0, 0];
used ← SIZE[BinderArgs];
IF type # NIL
THEN
BEGIN
args.type ← used;
CopyRope[from: type, to: @pkt[used]];
used ← used + SIZE[StringBody[type.Length[]]];
END;
IF instance # NIL
THEN
BEGIN
args.instance ← used;
CopyRope[from: instance, to: @pkt[used]];
used ← used + SIZE[StringBody[instance.Length[]]];
END;
IF used > pktSize THEN ERROR;
[resultLength,] ← RPCLupine.Call[pkt, used, pktSize];
END;
RETURN[LOOPHOLE[@pkt.data, POINTER TO BinderResult]^];
END;
ImportInstance: PUBLIC TYPE = RPCInternal.ImportInstance;
debugRemoteBind: BOOLEAN ← FALSE;
LocateImportInstance:
PROC[instance: Rope.
ROPE]
RETURNS[host: RPCPkt.Machine] =
BEGIN
info: InstanceInfo;
isRName: BOOLEAN;
[info, isRName, host] ← LocateInstance[instance];
SELECT info
FROM
ok => NULL;
badName => ERROR RPC.ImportFailed[badInstance];
allDown => ERROR RPC.ImportFailed[communications];
noAddress => ERROR RPC.ImportFailed[unbound];
ENDCASE => ERROR;
END;
ImportInterface:
PUBLIC
PROC[interface: RPC.InterfaceName,
stubProtocol: RPC.VersionRange,
localOnly: BOOLEAN ← FALSE]
RETURNS[ handle: REF ImportInstance ] =
BEGIN
host: RPCPkt.Machine;
dispatcher: RPCPkt.DispatcherDetails;
IF interface.type = NIL THEN ERROR RPC.ExportFailed[badType];
IF debugRemoteBind AND NOT localOnly
THEN
BEGIN
host ← LocateImportInstance[interface.instance];
dispatcher ← TryBinding[host, interface, stubProtocol];
END
ELSE
BEGIN
host ← LocateImportInstance[interface.instance];
IF localOnly AND host # myMachine
THEN ERROR RPC.ImportFailed[unbound]
ELSE dispatcher ← TryBinding[host, interface, stubProtocol];
END;
RETURN[NEW[ImportInstance ← [host,dispatcher]]]
END;
TryBinding:
PROC[host: RPCPkt.Machine,
impName: RPC.InterfaceName,
stubProtocol: RPC.VersionRange]
RETURNS[RPCPkt.DispatcherDetails] = CHECKED
BEGIN
expDetails: BinderResult;
expDetails ← RemoteBind[host, impName.type, impName.instance !
RPC.CallFailed =>
IF why = busy THEN RETRY ELSE ERROR RPC.ImportFailed[communications] ];
IF expDetails.dispatcher.dispatcherID = RPCPkt.noDispatcher
THEN ERROR RPC.ImportFailed[unbound];
IF stubProtocol # RPC.matchAllVersions
AND expDetails.stubProtocol # RPC.matchAllVersions
AND ( stubProtocol.first > expDetails.stubProtocol.last
OR stubProtocol.last < expDetails.stubProtocol.first )
THEN ERROR RPC.ImportFailed[stubProtocol];
IF impName.version # RPC.matchAllVersions
AND impName.version.first > impName.version.last
THEN ERROR RPC.ImportFailed[badVersion];
IF impName.version # RPC.matchAllVersions
AND expDetails.version # RPC.matchAllVersions
AND ( impName.version.first > expDetails.version.last
OR impName.version.last < expDetails.version.first )
THEN ERROR RPC.ImportFailed[wrongVersion];
RETURN[expDetails.dispatcher];
END;
UnimportInterface:
PUBLIC
PROC[handle:
REF ImportInstance]
RETURNS[REF ImportInstance] =
{ RETURN[NIL] };
myMachine: RPCPkt.Machine;
-- ******** Initialization ******** --
Initialize:
ENTRY
PROC =
BEGIN
BEGIN
myAddr: PupDefs.PupAddress = PupDefs.AnyLocalPupAddress[[0,0]];
myMachine ← [net: myAddr.net, host: myAddr.host];
END;
IF exportTable.used = 0
THEN
BEGIN
binderMDS: CARDINAL = PrincOpsUtils.HighHalf[LONG[LOOPHOLE[1,POINTER]]];
exportTable[binderHint] ←
[binderID, Binder, binderMDS, ["Binder",NIL,[0,0]], [0,0]];
exportTable.used ← 1;
END;
END;
Restart:
ENTRY
PROC =
{ lastExportID ← BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]] };
Initialize[];
DO STOP; Restart[]; ENDLOOP;
END.