RPCBinding.mesa - Binding primitives (& signals)
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bob Hagmann August 7, 1985 10:40:13 am PDT
Russ Atkinson (RRA) October 29, 1985 8:59:12 am PST
Hal Murray, January 22, 1986 4:37:00 pm PST
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 [AuthenticateFailure, CallFailed, CallFailure, EncryptionKey, ExportFailed, ExportFailure, ImportFailed, ImportFailure, InterfaceName, matchAllVersions, Principal, VersionRange],
RPCInternal USING [ExportInstance, ExportTable, ImportInstance],
RPCInternalExtras,
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, RPCInternalExtras, RPCLupine = {
Signals (formerly in RPCSignals)
AuthenticateFailed: PUBLIC SAFE ERROR[why: RPC.AuthenticateFailure] = CODE;
CallFailed: PUBLIC SAFE SIGNAL[why: RPC.CallFailure] = CODE;
ExportFailed: PUBLIC SAFE ERROR[why: RPC.ExportFailure] = CODE;
ImportFailed: PUBLIC SAFE ERROR[why: RPC.ImportFailure] = CODE;
LongEquivalent: PROC [a: LONG STRING, b: Rope.ROPE] RETURNS [BOOL] = {
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 {
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];
};
ENDLOOP;
RETURN[TRUE]
};
CopyRope: PROC [from: Rope.ROPE, to: LONG POINTER] = {
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]]];
};
InstanceInfo: TYPE = { ok, badName, allDown, noAddress };
LocateInstance: PROC [instance: Rope.ROPE] RETURNS [info: InstanceInfo, isRName: BOOL, host: RPCPkt.Machine] = {
connect: Rope.ROPENIL;
IF instance.Length[] > GVBasics.maxRNameLength THEN RETURN[badName,,];
isRName ← Rope.Find[instance, "."] >= 0;
IF isRName
THEN {
info: GVNames.ConnectInfo;
[info, connect] ← GVNames.GetConnect[instance];
SELECT info FROM
individual => NULL;
notFound, group => RETURN[badName,isRName,];
allDown => RETURN[allDown,isRName,];
ENDCASE => ERROR;
}
ELSE connect ← instance;
IF connect.Length[] = 0
THEN info ← IF isRName THEN noAddress ELSE badName
ELSE {
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];
};
};
******** 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 {
RETURN[GVBasics.MakeKey[text]]
};
ExportInterface: PUBLIC PROC [user: RPC.Principal, password: RPC.EncryptionKey, interface: RPC.InterfaceName, dispatcher: RPCLupine.Dispatcher, stubProtocol: RPC.VersionRange, localOnly: BOOLFALSE] RETURNS [ instance: ExportHandle ] = {
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 ~localOnly AND interface.instance # NIL THEN {
ENABLE UNWIND => [] ← UnexportInterface[instance];
prevHost: RPCPkt.Machine;
prevInfo: InstanceInfo;
isRName: BOOL;
[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 {
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;
}
ELSE ERROR RPC.ExportFailed[badInstance];
};
};
lastExportID: RPCPkt.DispatcherID ← BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]];
UID on this machine
AddToExportTable: PUBLIC ENTRY PROC [interface: RPC.InterfaceName, dispatcher: RPCLupine.Dispatcher, stubProtocol: RPC.VersionRange] RETURNS [ instance: ExportHandle ] = {
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];
};
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,],];
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: 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 = {
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]];
};
-- Server-implementation for binding calls --
LocalBind: ENTRY PROC [type, instance: LONG STRING] RETURNS [BinderResult] = {
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,]]]
};
RemoteBind: SAFE PROC [host: RPCPkt.Machine, type, instance: Rope.ROPE] RETURNS [BinderResult] = TRUSTED {
user-stub for binding calls
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];
{
used: CARDINAL;
args^ ← [binderProc, 0, 0];
used ← SIZE[BinderArgs];
IF type # NIL
THEN {
args.type ← used;
CopyRope[from: type, to: @pkt[used]];
used ← used + SIZE[StringBody[type.Length[]]];
};
IF instance # NIL
THEN {
args.instance ← used;
CopyRope[from: instance, to: @pkt[used]];
used ← used + SIZE[StringBody[instance.Length[]]];
};
IF used > pktSize THEN ERROR;
[resultLength,] ← RPCLupine.Call[pkt, used, pktSize];
};
RETURN[LOOPHOLE[@pkt.data, POINTER TO BinderResult]^];
};
ImportInstance: PUBLIC TYPE = RPCInternal.ImportInstance;
debugRemoteBind: BOOLFALSE;
LocateImportInstance: PROC [instance: Rope.ROPE] RETURNS [host: RPCPkt.Machine] = {
info: InstanceInfo;
isRName: BOOL;
[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;
};
ImportInterface: PUBLIC PROC [interface: RPC.InterfaceName, stubProtocol: RPC.VersionRange, localOnly: BOOLFALSE] RETURNS [ handle: REF ImportInstance ] = {
host: RPCPkt.Machine;
dispatcher: RPCPkt.DispatcherDetails;
IF interface.type = NIL THEN ERROR RPC.ExportFailed[badType];
IF debugRemoteBind AND NOT localOnly
THEN {
host ← LocateImportInstance[interface.instance];
dispatcher ← TryBinding[host, interface, stubProtocol];
}
ELSE {
host ← LocateImportInstance[interface.instance];
IF localOnly AND host # myMachine
THEN ERROR RPC.ImportFailed[unbound]
ELSE dispatcher ← TryBinding[host, interface, stubProtocol];
};
RETURN[NEW[ImportInstance ← [host,dispatcher]]]
};
TryBinding: PROC [host: RPCPkt.Machine, impName: RPC.InterfaceName, stubProtocol: RPC.VersionRange] RETURNS [RPCPkt.DispatcherDetails] = CHECKED {
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];
};
UnimportInterface: PUBLIC PROC [handle: REF ImportInstance] RETURNS [REF ImportInstance] = {
RETURN[NIL];
};
myMachine: RPCPkt.Machine;
-- ******** Initialization ******** --
Initialize: ENTRY PROC = {
{
myAddr: PupDefs.PupAddress = PupDefs.AnyLocalPupAddress[[0,0]];
myMachine ← [net: myAddr.net, host: myAddr.host];
};
IF exportTable.used = 0 THEN {
binderMDS: CARDINAL = PrincOpsUtils.HighHalf[LONG[LOOPHOLE[1,POINTER]]];
exportTable[binderHint] ← [binderID, Binder, binderMDS, ["Binder",NIL,[0,0]], [0,0]];
exportTable.used ← 1;
};
};
Restart: ENTRY PROC = {
lastExportID ← BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]];
};
Initialize[];
DO STOP; Restart[]; ENDLOOP;
}.
Bob Hagmann August 7, 1985 10:40:13 am PDT
changes to: AddToExportTable, RPCBinding, ExportHandle, DIRECTORY