<> <> <> DIRECTORY BasicTime USING [FromPupTime, GMT, Now, Period], FSBackdoor USING [Version], FSRemoteFile USING [LookupResult], PrincOps USING [zEXCH], Process USING [Detach, SecondsToTicks], PupDefs USING [GetFreePupBuffer, GetHopsToNetwork, GetPupAddress, MsToTocks, PupAddress, PupNameTrouble, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, SetPupContentsBytes], PupTypes USING [fillInSocketID, Pair, PupSocketID, PupType], Rope USING [Equal, Fetch, Length, ROPE]; FSFileLookupImpl: CEDAR MONITOR IMPORTS BasicTime, Process, PupDefs, Rope EXPORTS FSRemoteFile = BEGIN <> Lookup: PUBLIC PROC [server, file: Rope.ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: BasicTime.GMT, count: INT] = BEGIN sI: REF ServerInfo; [result, sI] _ FindServer[server]; IF result = ok THEN BEGIN [result, version, create, count] _ DoFileLookup[sI.addr, file]; SetState[sI, result]; END; END; <> serverList: REF ServerInfo _ NIL; -- list of known servers; packetCount: LONG CARDINAL _ 0; -- outgoing; also used as pupID ReverseCardinal: TYPE = MACHINE DEPENDENT RECORD [high, low: CARDINAL]; ServerInfo: TYPE = RECORD [ name: Rope.ROPE, addr: PupDefs.PupAddress, state: FSRemoteFile.LookupResult, -- can be noResponse, noSuchPort, or ok time: BasicTime.GMT, -- GMT of last ok or first not ok next: REF ServerInfo]; FindServer: ENTRY PROC [server: Rope.ROPE] RETURNS [result: FSRemoteFile.LookupResult, info: REF ServerInfo] = TRUSTED BEGIN result _ ok; FOR info _ serverList, info.next UNTIL info = NIL DO IF Rope.Equal[info.name, server, FALSE] THEN {result _ info.state; RETURN}; ENDLOOP; <> BEGIN LookupFileSocket: PupTypes.PupSocketID = [0, 61B]; addr: PupDefs.PupAddress; addr _ PupDefs.GetPupAddress[LookupFileSocket, server ! PupDefs.PupNameTrouble => { SELECT code FROM noRoute, noResponse => result _ noResponse; errorFromServer => result _ noSuchServer; ENDCASE => ERROR; CONTINUE } ]; IF result # noSuchServer THEN BEGIN info _ NEW [ServerInfo _ [name: server, addr: addr, state: result, time: BasicTime.Now[], next: serverList]]; IF serverList = NIL THEN Process.Detach[FORK ServerCollector[]]; serverList _ info; END; END; END; DoFileLookup: PROC [addr: PupDefs.PupAddress, file: Rope.ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: BasicTime.GMT, count: INT] = TRUSTED BEGIN LookupFileType: PupTypes.PupType = LOOPHOLE[200B]; LookupFileReplyType: PupTypes.PupType = LOOPHOLE[201B]; LookupFileErrorType: PupTypes.PupType = LOOPHOLE[202B]; tries: CARDINAL = 4; msWait: CARDINAL = 2000+500*MIN[8, PupDefs.GetHopsToNetwork[addr.net]]; s: PupDefs.PupSocket; fileNameChars: CARDINAL = Rope.Length[file]; result _ noResponse; -- assume result s _ PupDefs.PupSocketMake [local: PupTypes.fillInSocketID, remote: addr, ticks: PupDefs.MsToTocks[msWait] ]; THROUGH [1..tries] DO b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; b.pupType _ LookupFileType; b.pupID _ PupIDFromLongCardinal[packetCount _ packetCount + 1]; FOR i: CARDINAL IN [0 .. fileNameChars) DO b.pupChars[i] _ Rope.Fetch[file, i]; ENDLOOP; PupDefs.SetPupContentsBytes[b, fileNameChars]; s.put[b]; BEGIN r: PupDefs.PupBuffer = s.get[]; IF r = NIL THEN LOOP; -- no response yet SELECT r.pupType FROM LookupFileReplyType => BEGIN rPtr: LONG POINTER TO MACHINE DEPENDENT RECORD[v: CARDINAL, c, l: ReverseCardinal] = LOOPHOLE[@r.pupBody]; result _ ok; version _ [rPtr.v]; create _ BasicTime.FromPupTime[LongCardFromReverseCardinal[rPtr.c]]; count _ LongCardFromReverseCardinal[rPtr.l]; END; LookupFileErrorType => result _ noSuchFile; error => SELECT r.errorCode FROM noProcessPupErrorCode => result _ noSuchPort; cantGetTherePupErrorCode => NULL; ENDCASE => {PupDefs.ReturnFreePupBuffer[r]; LOOP}; ENDCASE => {PupDefs.ReturnFreePupBuffer[r]; LOOP}; PupDefs.ReturnFreePupBuffer[r]; EXIT; END; ENDLOOP; PupDefs.PupSocketDestroy[s]; END; PupIDFromLongCardinal: PROC [s: LONG CARDINAL] RETURNS [PupTypes.Pair] = TRUSTED MACHINE CODE {PrincOps.zEXCH}; LongCardFromReverseCardinal: PROC [r: ReverseCardinal] RETURNS [LONG CARDINAL] = TRUSTED MACHINE CODE {PrincOps.zEXCH}; SetState: ENTRY PROC [info: REF ServerInfo, state: FSRemoteFile.LookupResult] = BEGIN info.time _ BasicTime.Now[]; IF state = noSuchPort THEN info.state _ noSuchPort; END; ServerCollector: ENTRY PROC = BEGIN forOneMinute: CONDITION _ [Process.SecondsToTicks[60]]; UNTIL serverList = NIL DO last: REF ServerInfo _ NIL; info: REF ServerInfo; WAIT forOneMinute; FOR info _ serverList, info.next UNTIL info = NIL DO age: INT = BasicTime.Period[info.time, BasicTime.Now[]]; IF (info.state # ok AND age > 300) OR (age > 1800) THEN { IF last=NIL THEN serverList _ info.next ELSE last.next _ info.next; } ELSE last _ info; ENDLOOP; ENDLOOP; END; END.