-- Transport Mechanism - Location of server by client --
-- [Juniper]<DMS>MS>Locate.mesa
-- Andrew Birrell 18-Mar-81 17:55:30 --
DIRECTORY
BodyDefs USING [Connect, maxRNameLength, RName],
LocateDefs USING [FoundServerInfo, FoundState],
NameInfoDefs USING [Close, Enumerate, GetConnect, GetMembers,
MemberInfo, NameType, RListHandle],
ProtocolDefs USING [Connect, Init, IsLocal, maxConnectLength,
RegServerEnquirySocket, RegServerPollingSocket],
PupDefs USING [EnumeratePupAddresses, GetFreePupBuffer,
GetPupAddress, PupAddress, PupBuffer,
PupNameTrouble, PupRouterSendThis, PupSocket,
PupSocketDestroy, PupSocketID, PupSocketMake,
ReturnFreePupBuffer, SecondsToTocks,
SetPupContentsWords],
PupTypes USING [fillInSocketID, PupSocketID],
Storage USING[ Node, Free ],
String USING [AppendString, EquivalentString];
Locate: MONITOR
IMPORTS NameInfoDefs, ProtocolDefs, PupDefs, Storage, String
EXPORTS LocateDefs =
BEGIN
ReplyInfo: TYPE = RECORD[ found: BOOLEAN, where: PupDefs.PupAddress ];
AcceptReplies: PROCEDURE[ socket: PupDefs.PupSocket, sent: CARDINAL,
accept: PROCEDURE[PupDefs.PupAddress]RETURNS[BOOLEAN] ]
RETURNS[ info: ReplyInfo ] =
BEGIN
bestHops: CARDINAL ← LAST[CARDINAL];
info.found ← FALSE;
UNTIL info.found
OR sent = 0 -- we may finish early or late, but it will be near enough--
DO BEGIN
b: PupDefs.PupBuffer = socket.get[];
IF b = NIL
THEN EXIT
ELSE BEGIN
sent ← sent-1;
SELECT b.pupType FROM
iAmEcho =>
BEGIN
info.where ← b.source;
IF accept[info.where] THEN info.found ← TRUE
END;
ENDCASE;
PupDefs.ReturnFreePupBuffer[b];
END;
END;
ENDLOOP;
END;
SendEnquiry: PROCEDURE[addr: PupDefs.PupAddress, from: PupDefs.PupAddress] =
BEGIN
b: PupDefs.PupBuffer;
b ← PupDefs.GetFreePupBuffer[];
b.source ← from;
b.pupType ← echoMe;
b.dest ← addr;
-- force socket number, because database may not have GV test-mode socket numbers --
b.dest.socket ← ProtocolDefs.RegServerPollingSocket;
PupDefs.SetPupContentsWords[b, 0];
PupDefs.PupRouterSendThis[b];
END;
EnquiryInfo: TYPE = RECORD[ outcome:{ done, down, badName },
local: BOOLEAN, sent: CARDINAL];
gvName: STRING = "gv.gv"; -- NA for registration servers--
NoGVRS: SIGNAL = CODE; -- debugging; may be resumed --
GetGVRegServer: PROCEDURE[ from: PupDefs.PupAddress ]
RETURNS[ info: EnquiryInfo ] =
BEGIN
Work: PROCEDURE[addr: PupDefs.PupAddress] RETURNS[ BOOLEAN ] =
BEGIN
-- don't trust socket number given by NLS (?) --
addr.socket ← ProtocolDefs.RegServerEnquirySocket;
SendEnquiry[addr, from]; info.sent ← info.sent+1;
RETURN[FALSE]
END;
GVRS: STRING = "GrapevineRServer"L;
info ← [done, FALSE, 0];
-- hack to allow test servers anywhere on my net --
[] ← Work[ [net:[0], host:[0],
socket: ProtocolDefs.RegServerEnquirySocket ] ];
-- end hack --
BEGIN
ENABLE PupDefs.PupNameTrouble =>
IF code = errorFromServer
THEN BEGIN SIGNAL NoGVRS[]; GOTO bad END
ELSE GOTO down;
[] ← PupDefs.EnumeratePupAddresses[GVRS, Work ];
EXITS
down => info.outcome ← down;
bad => info.outcome ← badName;
END;
END;
GetGroupInfo: PROC[ who, local: BodyDefs.RName,
from: PupDefs.PupAddress]
RETURNS[ info: EnquiryInfo ] =
BEGIN
mInfo: NameInfoDefs.MemberInfo = NameInfoDefs.GetMembers[who];
WITH m: mInfo SELECT FROM
notFound => info ← [badName,FALSE,0];
allDown => info ← [down,FALSE,0];
individual => info ← [badName,FALSE,0];
group =>
BEGIN
apparent: CARDINAL ← 0;
actual: CARDINAL ← 0;
Count: PROC[member:BodyDefs.RName]RETURNS[done:BOOLEAN] =
{ apparent ← apparent + 1; done ← FALSE };
addresses: DESCRIPTOR FOR ARRAY OF PupDefs.PupAddress;
Access: PROC[member:BodyDefs.RName]RETURNS[done:BOOLEAN] =
BEGIN
cInfo: NameInfoDefs.NameType;
connect: ProtocolDefs.Connect = [ProtocolDefs.maxConnectLength];
done ← FALSE;
cInfo ← NameInfoDefs.GetConnect[member, connect];
SELECT cInfo FROM
individual =>
BEGIN
PupDefs.GetPupAddress[@(addresses[actual]), connect !
PupDefs.PupNameTrouble => GOTO cant];
actual ← actual + 1;
EXITS cant => NULL;
END;
ENDCASE => NULL -- ignore others --;
END;
NameInfoDefs.Enumerate[m.members, Count];
addresses ← DESCRIPTOR[Storage.Node[apparent*
SIZE[PupDefs.PupAddress]],
apparent];
NameInfoDefs.Enumerate[m.members, Access];
NameInfoDefs.Close[m.members];
info ← [done,FALSE,0];
FOR i: CARDINAL IN [0..actual)
DO IF local = NIL
THEN { SendEnquiry[addresses[i], from]; info.sent ← info.sent+1 }
ELSE BEGIN
IF ProtocolDefs.IsLocal[addresses[i]]
THEN BEGIN
info.local ← TRUE;
local.length ← 0; String.AppendString[local, who];
END;
END;
ENDLOOP;
Storage.Free[BASE[addresses]];
END;
ENDCASE => ERROR;
END;
FindRegServer: PUBLIC PROCEDURE[ who: BodyDefs.RName,
accept: PROCEDURE[PupDefs.PupAddress]RETURNS[BOOLEAN] ]
RETURNS[ foundInfo: LocateDefs.FoundServerInfo ] =
BEGIN
-- find a registration server for given R-Name --
sep: CHARACTER = '.; -- SN sep NA --
rPtr: CARDINAL;
NA: BodyDefs.RName = [BodyDefs.maxRNameLength];
-- parse to find registry name --
rPtr ← who.length;
DO IF rPtr = 0 THEN RETURN[ [notFound[]] ];
rPtr←rPtr-1;
IF who[rPtr] = sep THEN EXIT;
ENDLOOP;
NA.length ← 0;
FOR rPtr←rPtr+1, rPtr+1 WHILE rPtr # who.length
DO NA[NA.length] ← who[rPtr]; NA.length←NA.length+1 ENDLOOP;
String.AppendString[NA, ".GV"L];
foundInfo ← FindNearestServer[NA, accept];
END; --FindRegServer--
FindNearestServer: PUBLIC PROCEDURE[list: BodyDefs.RName,
accept: PROCEDURE[PupDefs.PupAddress]RETURNS[BOOLEAN] ]
RETURNS[info: LocateDefs.FoundServerInfo] =
BEGIN
socket: PupDefs.PupSocket = PupDefs.PupSocketMake[
local: PupTypes.fillInSocketID,
remote:, ticks: PupDefs.SecondsToTocks[1] ];
from: PupDefs.PupAddress = socket.getLocalAddress[];
THROUGH [1..3] -- re-tries for lost packets --
DO BEGIN
sendInfo: EnquiryInfo = IF String.EquivalentString[list, gvName]
THEN GetGVRegServer[from]
ELSE GetGroupInfo[list,NIL,from];
SELECT sendInfo.outcome FROM
badName => { info ← [notFound[]]; EXIT };
down => { info ← [allDown[]]; EXIT };
done =>
BEGIN
reply: ReplyInfo = AcceptReplies[socket, sendInfo.sent, accept];
IF reply.found
THEN { info ← [found[reply.where]]; EXIT }
ELSE NULL --continue round loop--
END;
ENDCASE => ERROR;
END;
REPEAT
FINISHED => info ← [allDown[]]
ENDLOOP;
PupDefs.PupSocketDestroy[socket];
END --FindNearestServer--;
FindLocalServer: PUBLIC PROCEDURE[list, local: BodyDefs.RName]
RETURNS[ LocateDefs.FoundState ] =
BEGIN
sendInfo: EnquiryInfo = GetGroupInfo[list, local, ];
SELECT sendInfo.outcome FROM
badName => RETURN[ notFound ];
down => RETURN[ allDown ];
done => RETURN[ IF sendInfo.local THEN found ELSE notFound ];
ENDCASE => ERROR;
END;
AcceptFirst: PUBLIC PROCEDURE[PupDefs.PupAddress]RETURNS[BOOLEAN] =
BEGIN
RETURN[TRUE]
END;
ProtocolDefs.Init[];
END.