STPFSRemoteLookupImpl.mesa
Copyright Ó 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Schroeder, November 8, 1983 1:53 pm
Levin, June 24, 1983 5:38 pm
Russ Atkinson, March 18, 1985 10:41:54 pm PST
Bob Hagmann November 14, 1986 8:23:59 am PST
Hal Murray, June 3, 1986 4:33:20 pm PDT
Doug Wyatt, December 12, 1986 3:00:45 pm PST
Demers, October 27, 1987 0:46:09 am PST
DIRECTORY
Basics USING [FWORD, Card32FromF, Card16FromH],
BasicTime USING [FromPupTime, GMT, Now, nullGMT, Period],
FSBackdoor USING [Version],
FSRemoteFileBackdoor USING [ServerHandle],
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],
STPFSRemoteFile USING [LookupResult, ServerData]
;
STPFSRemoteLookupImpl:
CEDAR
MONITOR
IMPORTS Basics, BasicTime, Process, PupHop, PupName, PupSocket, Rope, RuntimeError
EXPORTS STPFSRemoteFile
= {
CARD: TYPE = LONG CARDINAL;
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
FileLookup protocol implementation
serverList: REF ServerInfo ← NIL; -- list of known servers;
keepingList:
BOOL ←
TRUE;
keepingList => 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: STPFSRemoteFile.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 [h: FSRemoteFileBackdoor.ServerHandle]
RETURNS [result: STPFSRemoteFile.LookupResult, info:
REF ServerInfo] =
TRUSTED {
ENABLE UNWIND => NULL;
server: ROPE ← h.name;
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
{
data: STPFSRemoteFile.ServerData ~ NARROW[h.data];
name: ROPE ← data.serverPupName;
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: STPFSRemoteFile.LookupResult, version: FSBackdoor.Version, create:
GMT, count:
INT] =
TRUSTED {
tries: CARDINAL = 4;
msWait: CARDINAL ← PupHop.InitialTimeout[addr.net, 2000];
id: Basics.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 ← [Basics.Card16FromH[b.fileLookupReply.version]];
create ← BasicTime.FromPupTime[Basics.Card32FromF[b.fileLookupReply.createTime]
! RuntimeError.UNCAUGHT => {create ← BasicTime.nullGMT; CONTINUE}];
count ← Basics.Card32FromF[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: STPFSRemoteFile.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;
DKW: formerly IF state = noSuchPort THEN info.state ← noSuchPort;
};
pollingInterval:
NAT ← 5;
seconds to wait before polling
okTimeout:
NAT ← 1800;
seconds to wait before having a cached OK server evaporate
noResponseTimeout:
NAT ← 30;
seconds to wait before having a noResponse evaporate
noSuchPortTimeout:
NAT ← 30;
seconds to wait before having a noSuchPort evaporate
errorTimeout:
NAT ← 30;
seconds to wait before having any other error evaporate
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;
};
}.