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