-- Transport Mechanism - Location of server by client -- -- [Juniper]<DMS>MS>Locate.mesa -- Andrew Birrell 20-Mar-81 16:56:49 -- -- Mike Schroeder March 21, 1981 10:59 AM -- -- Hankins: 13-Jun-84 17:22:48 Klamath update (pup changes) DIRECTORY BodyDefs USING [Connect, maxRNameLength, RName], Buffer USING [AccessHandle, GetBuffer, MakePool, ReturnBuffer], LocateDefs USING [FoundServerInfo, FoundState], NameInfoDefs USING [ Close, Enumerate, GetConnect, GetMembers, MemberInfo, NameType, RListHandle], Process USING [MsecToTicks, SetTimeout], ProtocolDefs USING [ Connect, Init, IsLocal, maxConnectLength, RegServerEnquirySocket, RegServerPollingSocket], PupDefs USING [ EnumeratePupAddresses, GetHopsToNetwork, GetPupAddress, PupAddress, PupBuffer, PupNameTrouble, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketID, PupSocketKick, PupSocketMake, SetPupContentsWords, veryLongWait], PupTypes USING [fillInSocketID, PupSocketID], Storage USING [Node, Free], String USING [AppendString, EquivalentString]; Locate: MONITOR IMPORTS Buffer, NameInfoDefs, Process, ProtocolDefs, PupDefs, Storage, String EXPORTS LocateDefs = BEGIN HopCount: TYPE = [0..37777B]; AddressRec: TYPE = RECORD [ addr: PupDefs.PupAddress, hops: HopCount, reply: BOOLEAN]; AddressTable: TYPE = DESCRIPTOR FOR ARRAY OF AddressRec; AddressInfo: TYPE = RECORD [ outcome: {done, down, badName}, preSorted: BOOLEAN, found: CARDINAL, addresses: AddressTable]; ReplyInfo: TYPE = RECORD [found: BOOLEAN, where: PupDefs.PupAddress]; bufferPool: Buffer.AccessHandle; Prod: PROCEDURE [ socket: PupDefs.PupSocket, addrInfo: AddressInfo, accept: PROCEDURE [PupDefs.PupAddress] RETURNS [BOOLEAN]] RETURNS [info: ReplyInfo] = BEGIN -- This is complicated! -- The parameters are a table of candidate addresses, and a socket. -- The prodder process sends out "echoMe" pups to the addresses; -- the slurper process accepts "iAmEcho" replies, and marks the table. -- The original process reads the marks from the table, and calls the -- client's "accept" procedure to see if this address is ok. -- This satisfies various constraints about not hogging pup buffers. -- The "echoMe" packets are sent out to closer addresses first; they're -- not sent after the client has accepted an address. -- Everywhere, the Monitor is not locked while doing long waits. from: PupDefs.PupAddress = socket.getLocalAddress[]; replyRecvd: BOOLEAN ← FALSE; -- "reply-waiting" flag -- cond: CONDITION; -- notified when reply comes in -- pleaseDie: BOOLEAN ← FALSE; -- for killing auxiliary processes -- DeathWish: ENTRY PROC RETURNS [BOOLEAN] = INLINE {RETURN[pleaseDie]}; KillSiblings: ENTRY PROC = INLINE { pleaseDie ← TRUE; PupDefs.PupSocketKick[socket]}; NoteReply: ENTRY PROC [i: CARDINAL, addr: PupDefs.PupAddress] = BEGIN -- caller promises that "i" is in range -- IF addrInfo.addresses[i].addr.host = 0 THEN { addrInfo.addresses[i].addr.host ← addr.host; addrInfo.addresses[i].addr.net ← addr.net}; addrInfo.addresses[i].reply ← TRUE; IF NOT replyRecvd THEN NOTIFY cond; replyRecvd ← TRUE; END; TestReply: ENTRY PROC [i: CARDINAL] RETURNS [yes: BOOLEAN] = INLINE { yes ← addrInfo.addresses[i].reply; addrInfo.addresses[i].reply ← FALSE}; More: ENTRY PROC RETURNS [yes: BOOLEAN] = { IF NOT replyRecvd THEN WAIT cond; yes ← replyRecvd; replyRecvd ← FALSE}; SendProd: PROCEDURE = BEGIN sent: CARDINAL ← 0; lastHops: CARDINAL = 3; --sorting limit-- FOR wantedHops: CARDINAL IN [0..lastHops] DO lastPass: BOOLEAN = (wantedHops = lastHops); FOR i: CARDINAL IN [0..addrInfo.found) DO IF DeathWish[] OR sent >= addrInfo.found THEN GOTO getUsOutOfHere; IF addrInfo.preSorted OR (lastPass AND addrInfo.addresses[i].hops >= wantedHops) OR addrInfo.addresses[i].hops = wantedHops THEN BEGIN b: PupDefs.PupBuffer = Buffer.GetBuffer[ type: pup, aH: bufferPool, function: send, size: smallBuffer]; b.pup.source ← from; b.pup.pupType ← echoMe; b.pup.dest ← addrInfo.addresses[i].addr; -- force socket number, because database may not have GV test-mode socket numbers -- b.pup.dest.socket ← ProtocolDefs.RegServerPollingSocket; b.pup.pupWords[0] ← i; PupDefs.SetPupContentsWords[b, 1]; PupDefs.PupRouterSendThis[b]; sent ← sent + 1; END; REPEAT getUsOutOfHere => EXIT -- from outer loop! -- ENDLOOP; ENDLOOP; END; Slurp: PROCEDURE = BEGIN UNTIL DeathWish[] DO b: PupDefs.PupBuffer = socket.get[]; IF b # NIL THEN BEGIN SELECT b.pup.pupType FROM iAmEcho => IF b.pup.pupWords[0] < addrInfo.found THEN NoteReply[b.pup.pupWords[0], b.pup.source]; ENDCASE => NULL; Buffer.ReturnBuffer[b]; END; ENDLOOP; END; prodder: PROCESS = FORK SendProd[]; slurper: PROCESS = FORK Slurp[]; Process.SetTimeout[@cond, Process.MsecToTicks[1500]]; info.found ← FALSE; UNTIL info.found DO IF NOT More[] THEN EXIT -- time-out on "cond" -- ; FOR i: CARDINAL IN [0..addrInfo.found) UNTIL info.found DO IF TestReply[i] THEN BEGIN info.where ← addrInfo.addresses[i].addr; IF accept[info.where] THEN info.found ← TRUE; END; ENDLOOP; ENDLOOP; KillSiblings[]; JOIN prodder; JOIN slurper; END; GetGVRegServer: PROCEDURE RETURNS [info: AddressInfo] = BEGIN addressesInGVRS: CARDINAL = 10; -- we take the 10 closest -- Work: PROCEDURE [addr: PupDefs.PupAddress] RETURNS [BOOLEAN] = BEGIN -- terminate if we have enough addresses -- -- Note: EnumeratePupAddresses gives them closest first -- IF info.found = LENGTH[info.addresses] THEN RETURN[TRUE]; info.addresses[info.found] ← [ addr: [ net: addr.net, host: addr.host, socket: ProtocolDefs.RegServerEnquirySocket], hops: 0 -- ignored, because of "preSorted" flag -- , reply: FALSE]; info.found ← info.found + 1; RETURN[FALSE] END; -- Top-level contacting R-Servers. Any method is valid! -- Try local broadcast, and try Name Lookup Server. -- Failing only means R-Servers are inaccessible; name might be ok. BEGIN info ← [ done, TRUE, 0, DESCRIPTOR[ Storage.Node[addressesInGVRS * SIZE[AddressRec]], addressesInGVRS]]; [] ← Work[ [ net: [0], host: [0], -- local broadcast -- socket: ProtocolDefs.RegServerEnquirySocket]]; [] ← PupDefs.EnumeratePupAddresses[ "GrapevineRServer"L, Work ! PupDefs.PupNameTrouble => GOTO no]; EXITS no => NULL; END; END; GetGroupInfo: PROC [who, local: BodyDefs.RName] RETURNS [info: AddressInfo] = 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; Count: PROC [member: BodyDefs.RName] RETURNS [done: BOOLEAN] = { apparent ← apparent + 1; done ← FALSE}; 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 addr: PupDefs.PupAddress; addr.socket ← [0, 0]; PupDefs.GetPupAddress[ @addr, connect ! PupDefs.PupNameTrouble => GOTO cant]; info.addresses[info.found] ← [ addr: addr, hops: MIN[PupDefs.GetHopsToNetwork[addr.net], LAST[HopCount]], reply: FALSE]; info.found ← info.found + 1; IF local # NIL AND ProtocolDefs.IsLocal[addr] THEN { local.length ← 0; String.AppendString[local, member]}; EXITS cant => NULL; END; ENDCASE => NULL -- ignore others -- ; END; NameInfoDefs.Enumerate[m.members, Count]; info ← [ done, FALSE, 0, DESCRIPTOR[ Storage.Node[apparent * SIZE[AddressRec]], apparent]]; NameInfoDefs.Enumerate[m.members, Access]; NameInfoDefs.Close[m.members]; END; ENDCASE => ERROR; END; FindNearestServer: PUBLIC PROCEDURE [ list: BodyDefs.RName, accept: PROCEDURE [PupDefs.PupAddress] RETURNS [BOOLEAN]] RETURNS [info: LocateDefs.FoundServerInfo] = BEGIN gvName: STRING = "gv.gv"L; socket: PupDefs.PupSocket = PupDefs.PupSocketMake[ local: PupTypes.fillInSocketID, remote:, ticks: PupDefs.veryLongWait]; addrInfo: AddressInfo = IF String.EquivalentString[list, gvName] THEN GetGVRegServer[] ELSE GetGroupInfo[list, NIL]; SELECT addrInfo.outcome FROM badName => info ← [notFound[]]; down => info ← [allDown[]]; done => BEGIN THROUGH [1..4] -- re-tries for lost packets -- DO reply: ReplyInfo = Prod[socket, addrInfo, accept]; IF reply.found THEN {info ← [found[reply.where]]; EXIT} REPEAT FINISHED => info ← [allDown[]] ENDLOOP; Storage.Free[BASE[addrInfo.addresses]]; END; ENDCASE => ERROR; PupDefs.PupSocketDestroy[socket]; END --FindNearestServer-- ; FindLocalServer: PUBLIC PROCEDURE [list, local: BodyDefs.RName] RETURNS [LocateDefs.FoundState] = BEGIN addrInfo: AddressInfo = GetGroupInfo[list, local]; SELECT addrInfo.outcome FROM badName => RETURN[notFound]; down => RETURN[allDown]; done => { Storage.Free[BASE[addrInfo.addresses]]; RETURN[IF local.length # 0 THEN found ELSE notFound]}; 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-- AcceptFirst: PUBLIC PROCEDURE [PupDefs.PupAddress] RETURNS [BOOLEAN] = { RETURN[TRUE]}; ProtocolDefs.Init[]; bufferPool ← Buffer.MakePool[send: 3, receive: 0]; END.