<> <> <> <> <> DIRECTORY GVBasics USING [Connect, RName], GVLocate USING [FoundServerInfo, FoundState], GVNames USING [GetConnect, GetMembers, MemberInfo, NameType, RListHandle], GVProtocol USING [GetSocket, IsLocal], Process USING [MsecToTicks, SetTimeout ], Pup USING [Address, allHosts, allNets, nullAddress, Socket], PupBuffer USING [Buffer], PupHop USING [GetHop, Hop], PupName USING [Error, HisAddresses, NameLookup], PupSocket USING [AllocBuffer, CreateEphemeral, Destroy, Get, FreeBuffer, Kick, Send, SetNoErrors, SetUserHWords, Socket, waitForever], Rope USING [Cat, Equal, Fetch, Length, Substr]; GVLocateImpl: CEDAR MONITOR IMPORTS GVNames, GVProtocol, Process, PupHop, PupName, PupSocket, Rope EXPORTS GVLocate = BEGIN AddressInfo: TYPE = REF AddressInfoObject; AddressInfoObject: TYPE = RECORD[ preSorted: BOOLEAN, found: CARDINAL, addresses: SEQUENCE possible: CARDINAL OF AddressRec]; AddressRec: TYPE = RECORD[ addr: Pup.Address, hop: PupHop.Hop, reply: BOOLEAN]; ReplyInfo: TYPE = RECORD[ found: BOOLEAN, where: Pup.Address ]; Prod: PROC [socket: PupSocket.Socket, addrInfo: AddressInfo, accept: PROC [Pup.Address] RETURNS[BOOLEAN] ] RETURNS [info: ReplyInfo ] = { <> 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] = { RETURN[pleaseDie] }; KillSiblings: ENTRY PROC = { pleaseDie _ TRUE; PupSocket.Kick[socket] }; NoteReply: ENTRY PROC [i: CARDINAL, addr: Pup.Address] = { <> 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 ~replyRecvd THEN NOTIFY cond; replyRecvd _ TRUE; }; TestReply: ENTRY PROC [i: CARDINAL] RETURNS [yes: BOOLEAN] = { yes _ addrInfo.addresses[i].reply; addrInfo.addresses[i].reply _ FALSE }; More: ENTRY PROC RETURNS[yes: BOOLEAN] = CHECKED { IF ~replyRecvd THEN WAIT cond; yes _ replyRecvd; replyRecvd _ FALSE; }; SendProd: PROCEDURE = { sent: CARDINAL _ 0; lastHop: CARDINAL = 4; --sorting limit-- FOR wantedHop: CARDINAL IN [0..lastHop) DO lastPass: BOOLEAN = (wantedHop=lastHop-1); 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].hop>=wantedHop) OR addrInfo.addresses[i].hop = wantedHop THEN { b: PupBuffer.Buffer = PupSocket.AllocBuffer[socket]; b.type _ echoMe; b.dest _ addrInfo.addresses[i].addr; <> b.dest.socket _ GVProtocol.GetSocket[RSPoll]; b.hWord[0] _ i; PupSocket.SetUserHWords[b, 1]; PupSocket.Send[socket, b, b.dest]; sent _ sent + 1; }; REPEAT GetUsOutOfHere => EXIT -- from outer loop! ENDLOOP; ENDLOOP; }; Slurp: PROCEDURE = { UNTIL DeathWish[] DO b: PupBuffer.Buffer = PupSocket.Get[socket]; IF b # NIL THEN { SELECT b.type FROM iAmEcho => IF b.hWord[0] < addrInfo.found THEN NoteReply[b.hWord[0], b.source]; ENDCASE => NULL; PupSocket.FreeBuffer[b]; }; ENDLOOP; }; prodder: PROCESS; slurper: PROCESS; TRUSTED { prodder _ FORK SendProd[]; slurper _ FORK Slurp[]; Process.SetTimeout[@cond, Process.MsecToTicks[1500]]; }; info.found _ FALSE; UNTIL info.found DO IF ~More[] THEN EXIT-- time-out on "cond" --; FOR i: CARDINAL IN [0..addrInfo.found) UNTIL info.found DO IF TestReply[i] THEN { info.where _ addrInfo.addresses[i].addr; IF accept[info.where] THEN info.found _ TRUE; }; ENDLOOP; ENDLOOP; KillSiblings[]; TRUSTED { JOIN prodder; JOIN slurper; }; }; GetGVRegServer: PROC RETURNS[ info: AddressInfo ] = { addressesInGVRS: CARDINAL = 10; -- we take the 10 closest socket: Pup.Socket = GVProtocol.GetSocket[RSEnquiry]; addresses: LIST OF Pup.Address; <> info _ NEW[AddressInfoObject[addressesInGVRS]]; info.preSorted _ TRUE; info.found _ 0; info.addresses[0] _ [ addr: [net: Pup.allNets, host: Pup.allHosts, socket: socket], hop: PupHop.GetHop[Pup.allNets], reply: FALSE ]; info.found _ 1; addresses _ PupName.HisAddresses["GrapevineRServer", socket ! PupName.Error => CONTINUE ]; UNTIL addresses = NIL DO info.addresses[info.found] _ [ addr: addresses.first, hop: PupHop.GetHop[addresses.first.net], reply: FALSE ]; info.found _ info.found + 1; IF info.found = addressesInGVRS THEN EXIT; addresses _ addresses.rest; ENDLOOP; }; GetGroupInfo: PROC [who: GVBasics.RName] RETURNS[ state: GVLocate.FoundState, info: AddressInfo _ NIL, local: GVBasics.RName _ NIL] = { mInfo: GVNames.MemberInfo = GVNames.GetMembers[who]; SELECT mInfo.type FROM notFound, individual => state _ notFound; allDown => state _ allDown; group => { m: group GVNames.MemberInfo = NARROW[mInfo, group GVNames.MemberInfo]; state _ found; info _ NEW[AddressInfoObject[m.count]]; info.preSorted _ FALSE; info.found _ 0; FOR member: GVNames.RListHandle _ m.members, member.rest UNTIL member = NIL DO cInfo: GVNames.NameType; connect: GVBasics.Connect; [cInfo, connect] _ GVNames.GetConnect[member.first]; SELECT cInfo FROM individual => BEGIN addr: Pup.Address = PupName.NameLookup[connect, GVProtocol.GetSocket[RSEnquiry] ! PupName.Error => GOTO Cant]; info.addresses[info.found] _ [ addr: addr, hop: PupHop.GetHop[addr.net], reply: FALSE ]; info.found _ info.found + 1; IF GVProtocol.IsLocal[addr] THEN local _ member.first; EXITS Cant => NULL; END; ENDCASE => NULL -- ignore others --; ENDLOOP; }; ENDCASE => ERROR; }; FindNearestServer: PUBLIC PROC [ list: GVBasics.RName, accept: PROC [Pup.Address] RETURNS [BOOL] ] RETURNS [info: GVLocate.FoundServerInfo] = { addrInfo: AddressInfo; state: GVLocate.FoundState; IF list.Equal["gv.gv", FALSE] THEN { state _ found; addrInfo _ GetGVRegServer[]; } ELSE [state, addrInfo, ] _ GetGroupInfo[list]; SELECT state FROM notFound => RETURN[[notFound[]]]; allDown => RETURN[[allDown[]]]; found => { socket: PupSocket.Socket = PupSocket.CreateEphemeral[ remote: Pup.nullAddress, getTimeout: PupSocket.waitForever ]; PupSocket.SetNoErrors[socket]; FOR i: NAT IN [0..4) DO -- re-tries for lost packets reply: ReplyInfo = Prod[socket, addrInfo, accept]; IF reply.found THEN { PupSocket.Destroy[socket]; RETURN[[found[reply.where]]]; }; REPEAT FINISHED => { PupSocket.Destroy[socket]; RETURN[[allDown[]]]; }; ENDLOOP; }; ENDCASE => ERROR; }; FindLocalServer: PUBLIC PROC [list: GVBasics.RName] RETURNS [state: GVLocate.FoundState, local: GVBasics.RName] = { [state, , local] _ GetGroupInfo[list]; IF local = NIL THEN state _ notFound; }; FindRegServer: PUBLIC PROC [ who: GVBasics.RName, accept: PROC [Pup.Address] RETURNS [BOOL] ] RETURNS [foundInfo: GVLocate.FoundServerInfo] = { <> sep: CHARACTER = '.; -- SN sep NA length: INT = who.Length[]; FOR i: INT DECREASING IN [0..length) DO IF who.Fetch[i] = sep THEN RETURN[FindNearestServer[Rope.Cat[who.Substr[start: i+1, len: length-i-1], ".gv"], accept]]; REPEAT FINISHED => RETURN[[notFound[]]]; ENDLOOP; }; END.