<> <> <> <> <> <> <> <> DIRECTORY BasicTime USING [FromPupTime, GMT, Now, nullGMT, Period], Endian USING [FWORD, CardFromF], FSBackdoor USING [Version], FSRemoteFile USING [GetServerPupName, LookupResult], Process USING [Detach, Ticks, SecondsToTicks], Pup USING [Address], PupBuffer USING [Buffer], PupHop USING [InitialTimeout], PupName USING [NameLookup, Error], PupSocket USING [AllocBuffer, CopyRope, CreateEphemeral, Destroy, FreeBuffer, Get, GetUniqueID, Send, SetNoErrors, Socket], PupType USING [fileLookup, fileLookupError, fileLookupReply], PupWKS USING [fileLookup], Rope USING [Equal, ROPE], RuntimeError USING [UNCAUGHT]; FSFileLookupImpl: CEDAR MONITOR IMPORTS BasicTime, Endian, FSRemoteFile, Process, PupHop, PupName, PupSocket, Rope, RuntimeError EXPORTS FSRemoteFile = { CARD: TYPE = LONG CARDINAL; GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; <> Lookup: PUBLIC PROC [server, file: ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: GMT, count: INT] = { sI: REF ServerInfo; [result, sI] _ FindServer[server]; IF result = ok THEN { [result, version, create, count] _ DoFileLookup[sI.addr, file]; SetState[sI, result]; }; }; <> serverList: REF ServerInfo _ NIL; -- list of known servers; keepingList: BOOL _ TRUE; < cache responses to try to reduce name lookup traffic and useless traffic when a server is obviously down>> packetCount: CARD _ 0; -- outgoing; also used as pup id ServerInfo: TYPE = RECORD [ name: ROPE, addr: Pup.Address, state: FSRemoteFile.LookupResult, -- can be noResponse, noSuchPort, or ok timeOK: GMT, -- GMT of last ok timeErr: GMT, -- GMT of last error next: REF ServerInfo]; FindServer: ENTRY PROC [server: ROPE] RETURNS [result: FSRemoteFile.LookupResult, info: REF ServerInfo] = TRUSTED { ENABLE UNWIND => NULL; result _ ok; FOR info _ serverList, info.next UNTIL info = NIL DO IF Rope.Equal[info.name, server, FALSE] THEN {result _ info.state; RETURN}; ENDLOOP; <> { name: ROPE _ FSRemoteFile.GetServerPupName[server]; addr: Pup.Address; addr _ PupName.NameLookup[name, PupWKS.fileLookup ! PupName.Error => { SELECT code FROM errorFromServer => result _ noSuchServer; ENDCASE => result _ noResponse; CONTINUE } ]; IF result # noSuchServer THEN { time: GMT _ BasicTime.Now[]; info _ NEW [ServerInfo _ [ name: server, addr: addr, state: result, timeOK: IF result = ok THEN time ELSE BasicTime.nullGMT, timeErr: IF result # ok THEN time ELSE BasicTime.nullGMT, next: serverList]]; IF serverList = NIL THEN Process.Detach[FORK ServerCollector[]]; IF keepingList THEN serverList _ info; }; }; }; DoFileLookup: PROC [addr: Pup.Address, file: ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: GMT, count: INT] = TRUSTED { tries: CARDINAL = 4; msWait: CARDINAL _ PupHop.InitialTimeout[addr.net, 2000]; id: Endian.FWORD _ PupSocket.GetUniqueID[]; socket: PupSocket.Socket; result _ noResponse; -- assume result IF msWait = 0 THEN RETURN; socket _ PupSocket.CreateEphemeral[remote: addr, getTimeout: msWait]; PupSocket.SetNoErrors[socket]; FOR i: NAT IN [0..tries) DO b: PupBuffer.Buffer _ PupSocket.AllocBuffer[socket]; b.type _ PupType.fileLookup; b.id _ id; PupSocket.CopyRope[b, file]; PupSocket.Send[socket, b, addr]; b _ PupSocket.Get[socket]; IF b = NIL THEN LOOP; -- no response yet IF b.id # id THEN { PupSocket.FreeBuffer[b]; LOOP; }; SELECT b.type FROM PupType.fileLookupReply => { result _ ok; version _ [b.fileLookupReply.version]; create _ BasicTime.FromPupTime[Endian.CardFromF[b.fileLookupReply.createTime] ! RuntimeError.UNCAUGHT => {create _ BasicTime.nullGMT; CONTINUE}]; count _ Endian.CardFromF[b.fileLookupReply.length]; }; PupType.fileLookupError => result _ noSuchFile; error => SELECT b.error.code FROM noSocket => result _ noSuchPort; cantGetThere => NULL; ENDCASE => { PupSocket.FreeBuffer[b]; LOOP; }; ENDCASE => { PupSocket.FreeBuffer[b]; LOOP; }; PupSocket.FreeBuffer[b]; EXIT; ENDLOOP; PupSocket.Destroy[socket]; }; SetState: ENTRY PROC [info: REF ServerInfo, state: FSRemoteFile.LookupResult] = { ENABLE UNWIND => NULL; time: GMT _ BasicTime.Now[]; IF state = ok THEN info.timeOK _ time ELSE info.timeErr _ time; SELECT state FROM noResponse, noSuchPort => info.state _ state; ENDCASE; <> }; pollingInterval: NAT _ 5; <> okTimeout: NAT _ 1800; <> noResponseTimeout: NAT _ 30; <> noSuchPortTimeout: NAT _ 30; <> errorTimeout: NAT _ 30; <> ServerCollector: ENTRY PROC = { ENABLE UNWIND => NULL; UNTIL serverList = NIL DO last: REF ServerInfo _ NIL; forPolling: CONDITION _ [Process.SecondsToTicks[pollingInterval]]; WAIT forPolling; FOR info: REF ServerInfo _ serverList, info.next UNTIL info = NIL DO time: GMT = BasicTime.Now[]; ageOK: INT = BasicTime.Period[info.timeOK, time]; ageErr: INT = BasicTime.Period[info.timeErr, time]; { SELECT info.state FROM ok => IF ageOK > okTimeout THEN GO TO Splice; noResponse => IF ageErr > noResponseTimeout THEN GO TO Splice; noSuchPort => IF ageErr > noSuchPortTimeout THEN GO TO Splice; ENDCASE => IF ageErr > errorTimeout THEN GO TO Splice; last _ info; EXITS Splice => IF last=NIL THEN serverList _ info.next ELSE last.next _ info.next; }; ENDLOOP; ENDLOOP; }; }. <<>> <<>> <> <> <<>> <<>>