-- Transport Mechanism - Location of server by client -- -- [Juniper]MS>Locate.mesa -- Andrew Birrell 20-Mar-81 16:56:49 -- -- Mike Schroeder March 21, 1981 10:59 AM -- DIRECTORY BodyDefs USING [Connect, maxRNameLength, RName], 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, GetFreePupBuffer, GetHopsToNetwork, GetPupAddress, PupAddress, PupBuffer, PupNameTrouble, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketID, PupSocketKick, PupSocketMake, ReturnFreePupBuffer, SetPupContentsWords, veryLongWait], PupTypes USING [fillInSocketID, PupSocketID], Storage USING [Node, Free ], String USING [AppendString, EquivalentString]; Locate: MONITOR IMPORTS 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 ]; 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 = PupDefs.GetFreePupBuffer[]; b.source _ from; b.pupType _ echoMe; b.dest _ addrInfo.addresses[i].addr; -- force socket number, because database may not have GV test-mode socket numbers -- b.dest.socket _ ProtocolDefs.RegServerPollingSocket; b.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.pupType FROM iAmEcho => IF b.pupWords[0] < addrInfo.found THEN NoteReply[b.pupWords[0], b.source]; ENDCASE => NULL; PupDefs.ReturnFreePupBuffer[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] = BEGIN RETURN[TRUE] END; ProtocolDefs.Init[]; END.