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
Exported to FSRemoteFile
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;
FileLookup protocol implementation
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;
Didn't find an entry, so make one
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;