<> <> <> <> <> <> <> DIRECTORY BasicTime USING [FromPupTime, GMT, Now, nullGMT, Period], FSBackdoor USING [Version], FSRemoteFile USING [GetServerPupName, LookupResult], PrincOps USING [zEXCH], Process USING [Detach, Ticks, 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], RuntimeError USING [UNCAUGHT]; FSFileLookupImpl: CEDAR MONITOR IMPORTS BasicTime, FSRemoteFile, Process, PupDefs, 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 pupID ReverseCardinal: TYPE = MACHINE DEPENDENT RECORD [high, low: CARDINAL]; ServerInfo: TYPE = RECORD [ name: ROPE, addr: PupDefs.PupAddress, 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; <> { LookupFileSocket: PupTypes.PupSocketID = [0, 61B]; addr: PupDefs.PupAddress; addr _ PupDefs.GetPupAddress[LookupFileSocket, FSRemoteFile.GetServerPupName[server] ! PupDefs.PupNameTrouble => { 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: PupDefs.PupAddress, file: ROPE] RETURNS [result: FSRemoteFile.LookupResult, version: FSBackdoor.Version, create: GMT, count: INT] = TRUSTED { 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]; { r: PupDefs.PupBuffer = s.get[]; IF r = NIL THEN LOOP; -- no response yet SELECT r.pupType FROM LookupFileReplyType => { 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] ! RuntimeError.UNCAUGHT => {create _ BasicTime.nullGMT; CONTINUE}]; count _ LongCardFromReverseCardinal[rPtr.l]; }; 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; }; ENDLOOP; PupDefs.PupSocketDestroy[s]; }; PupIDFromLongCardinal: PROC [s: CARD] RETURNS [PupTypes.Pair] = TRUSTED MACHINE CODE {PrincOps.zEXCH}; LongCardFromReverseCardinal: PROC [r: ReverseCardinal] RETURNS [CARD] = TRUSTED MACHINE CODE {PrincOps.zEXCH}; 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; }; }. <<>> <<>> <> <> <<>> <<>>