FSRemoteFileImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
HGM, February 8, 1984 11:55:19 pm PST
Bob Hagmann, August 2, 1985 4:38:26 pm PDT
Doug Wyatt, November 27, 1984 3:53:09 pm PST -- added allDown test to Info
Russ Atkinson (RRA) April 8, 1986 12:59:54 pm PST
Tim Diebert: February 11, 1986 9:39:22 am PST
Hal Murray, June 24, 1986 9:11:54 pm PDT
DIRECTORY
BasicTime USING [GMT, Now, nullGMT, Pack, Unpack, Unpacked],
FS USING [Error, ErrorGroup, InfoProc, NameProc],
FSBackdoor USING [ErrorCode, ProduceError, Version],
FSName USING [BangStarFile, BangVersionFile, VersionFromRope],
FSPseudoServers USING [AvoidRemoteCheck, PseudoServerList, TranslateForRead, TranslateForWrite],
FSRemoteFile USING [ConfirmProc, Lookup, LookupResult],
FSReport USING [UnknownFile],
GVBasics USING [Connect],
GVNames USING [ConnectInfo, GetConnect],
IO USING [STREAM],
Process USING [CheckForAbort, Detach, Seconds, SecondsToTicks],
Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Length, Match, ROPE],
RuntimeError USING [UNCAUGHT],
STP USING [Close, ConfirmProcType, Continue, Create, Delete, DesiredProperties, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NoteFileProcType, Open, Rename, Retrieve, SetDesiredProperties, SetDirectory, Store, ValidProperties],
UserCredentials USING [Get];
FSRemoteFileImpl: CEDAR MONITOR
IMPORTS BasicTime, FS, FSBackdoor, FSName, FSPseudoServers, FSRemoteFile, FSReport, GVNames, Process, Rope, RuntimeError, STP, UserCredentials
EXPORTS FSRemoteFile
= {
GMT: TYPE = BasicTime.GMT;
Handle: TYPE = STP.Handle;
ROPE: TYPE = Rope.ROPE;
PseudoServerList: TYPE = FSPseudoServers.PseudoServerList;
STREAM: TYPE = IO.STREAM;
Version: TYPE = FSBackdoor.Version;
Exported to FSRemoteFile
Delete: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: FSRemoteFile.ConfirmProc] = {
Confirm: STP.ConfirmProcType = {
[file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM]
info: STP.FileInfo = STP.GetFileInfo[h];
created: GMT = FTPTimeToGMT[info.create];
SELECT TRUE FROM
matchFound => answer ← abort;
NOT timeSearch OR wantedCreatedTime = created => {
answer ← IF proc [FSName.VersionFromRope[info.version]] THEN do ELSE abort;
matchFound ← TRUE;
};
ENDCASE => answer ← skip;
};
matchFound: BOOLEANFALSE;
timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT);
stpCode: STP.ErrorCode ← noSuchFile;
h: Handle ← NIL;
innerDelete: PROC RETURNS [ok: BOOLTRUE] = {
tServer: ROPE ← FSPseudoServers.TranslateForWrite[UnbracketServer[server]];
h ← NIL;
{
ENABLE UNWIND => ReturnConnection[tServer, h];
h ← GetConnection[tServer, file, FALSE];
STP.Delete[h, file, Confirm
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF NOT matchFound THEN {
IF timeSearch THEN {
file ← FSName.BangStarFile[file];
STP.Delete[h, file, Confirm];
};
IF NOT matchFound THEN {
ReturnConnection[tServer, h];
stpCode ← noSuchFile;
RETURN [FALSE]};
};
};
ReturnConnection[tServer, h];
};
IF innerDelete[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, server, file, wantedCreatedTime];
};
EnumerateForInfo: PUBLIC PROC [server, pattern: ROPE, proc: FS.InfoProc] = {
NoteInfo: LocalEnumProc = {
[file: ROPE] RETURNS [continue: BOOL]
info: STP.FileInfo = STP.GetFileInfo[h];
created: BasicTime.GMT = FTPTimeToGMT[info.create];
continue ← IF proc[Rope.Concat[serverB, file], NIL, created, info.size, 0]
THEN yes ELSE no;
};
serverB: ROPE = BracketServer[server];
InnerEnumerate[server, pattern, NoteInfo, FALSE];
};
EnumerateForNames: PUBLIC PROC [server, pattern: ROPE, proc: FS.NameProc] = {
NoteName: LocalEnumProc = {
[file: ROPE] RETURNS [continue: BOOL]
continue ← IF proc[Rope.Concat[serverB, file]] THEN yes ELSE no;
};
serverB: ROPE = BracketServer[server];
InnerEnumerate[server, pattern, NoteName, TRUE];
};
LocalEnumProc: TYPE = PROC [h: Handle, file: ROPE] RETURNS [continue: STP.Continue];
InnerEnumerate: PUBLIC PROC [server, pattern: ROPE, Note: LocalEnumProc, namesOnly: BOOL] = {
This helpful procedure allows enumeration for either names or information.
stpCode: STP.ErrorCode ← noSuchFile;
h: Handle ← NIL;
notedOne: BOOLFALSE;
innerNote: STP.NoteFileProcType = {
Process.CheckForAbort[];
RRA: this allows us to abort during an enumeration, which is non-trivial
notedOne ← TRUE;
IF NOT Rope.Match["<*", file] THEN file ← Rope.Concat["<>", file];
RRA: certain STP servers, like Nebula, do not prepend "<>" if they do not support directories. Since FS demands this syntax, we should make sure that it is here.
continue ← Note[h, file];
};
innerBlock: PROC [eachName: ROPE] = {
h ← NIL;
{
ENABLE UNWIND => ReturnConnection[eachName, h];
h ← GetConnection[eachName, pattern, namesOnly];
STP.Enumerate[h, pattern, innerNote];
};
ReturnConnection[eachName, h];
};
serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
FOR each: LIST OF ROPE ← serverList, each.rest WHILE each # NIL DO
innerBlock[each.first
! STP.Error =>
SELECT TRUE FROM
code = noSuchFile =>
If we do not get a match then we yield nothing. If replicated, then there is also no reason to try other servers, since replication should not affect presence. Otherwise, we go on to the next server, provided that we have not yielded something from this server.
SELECT TRUE FROM
replicated, notedOne => GO TO NotFound;
ENDCASE => LOOP;
notedOne => {
If we have seen at least one file then we do NOT try other servers, which would royally screw up the enumeration.
stpCode ← code; EXIT};
each = serverList => {
If the auxilliary servers do not work, we report the error from the first server only. So we remember the error code here.
stpCode ← code; LOOP};
ENDCASE =>
Any other errors during auxiliiary severs searches just go on to try the next server in the list (if any).
LOOP;
];
If we continue here, we have enumerated the pattern all right, and have returned the connection.
RETURN;
ENDLOOP;
If we continue here, we have an error of some kind during the enumeration, so we report it.
IF stpCode # noSuchFile THEN ReportSTPError[stpCode, server, pattern, BasicTime.nullGMT];
EXITS NotFound => {};
};
Info: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] = {
ReportError: PROC [stpCode: STP.ErrorCode] = {
ReportSTPError[stpCode, server, file, wantedCreatedTime];
};
result: FSRemoteFile.LookupResult;
serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
allDown: BOOLTRUE;
FOR each: LIST OF ROPE ← serverList, each.rest WHILE each # NIL DO
eachServer: ROPE ← each.first;
last: BOOL ← each.rest = NIL;
[result, version, created, bytes] ← FSRemoteFile.Lookup[eachServer, file];
IF result#noResponse THEN allDown ← FALSE;
SELECT result FROM
noSuchFile => {
No such file was the response. This is a positive response that the named file was not present on the named server.
IF NOT replicated THEN LOOP;
If we are NOT assuming replicated files, then we go look for another server.
IF wantedCreatedTime = BasicTime.nullGMT THEN ReportError[noSuchFile];
If we wanted any date, and we are assuming replicated files, then we should just give up right now, since we will not find a match.
EXIT;
If we wanted a specific date, then we may just have a bad version number hint, so we exit to do the search the expensive way.
};
ok => {
We got a response that the file was present.
IF wantedCreatedTime = BasicTime.nullGMT THEN RETURN;
We were requested to find any date, so the one we got was OK.
IF wantedCreatedTime = created THEN RETURN;
We were asked for a specific date, and we got it, so its all OK.
IF NOT replicated THEN LOOP;
We were asked for a specific date, and the file was there, but the date was wrong, which means that the version # hint was bogus. If we assume the files are replicated, we should exit, since the version hint is wrong on the replicated servers as well. Otherwise, we loop, hoping that another server will have the correct hint and date as well.
EXIT};
ENDCASE;
ENDLOOP;
The line of code following fails if there is no response to a single packet info check. Tim D.
IF allDown THEN ReportError[noNameLookupResponse];
This call is for the general case. It is more expensive, of course, since we have to grab a connection for it. However, STPInfo can handle bogus version # hints.
[version, bytes, created] ← STPInfo[server, file, wantedCreatedTime];
};
Rename: PUBLIC PROC [server, fromFile: ROPE, fromCreated: GMT, toFile: ROPE, proc: FSRemoteFile.ConfirmProc] = {
stpCode: STP.ErrorCode ← noSuchFile;
version: Version = Info[server, fromFile, fromCreated].version;
innerRename: PROC RETURNS [ok: BOOLTRUE] = {
h: Handle ← NIL;
tServer: ROPE ← FSPseudoServers.TranslateForWrite[server];
{
ENABLE UNWIND => ReturnConnection[tServer, h];
h ← GetConnection[tServer, fromFile, TRUE];
fromFile ← FSName.BangVersionFile[fromFile, version];
STP.Rename[h, fromFile, toFile];
};
ReturnConnection[tServer, h];
};
IF proc[version] THEN {
IF innerRename[! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, server, toFile, BasicTime.nullGMT];
};
};
Retrieve: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: PROC[fullGName: ROPE, bytes: INT, created: GMT] RETURNS [STREAM]] = {
matchFound: BOOLFALSE;
timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT);
h: Handle ← NIL;
stpCode: STP.ErrorCode ← noSuchFile;
serverB: ROPE = BracketServer[server];
Confirm: STP.ConfirmProcType = {
[file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM]
info: STP.FileInfo = STP.GetFileInfo[h];
created: GMT = FTPTimeToGMT[info.create];
SELECT TRUE FROM
matchFound => answer ← abort;
NOT timeSearch OR wantedCreatedTime = created => {
localStream ← proc[Rope.Concat[serverB, file], info.size, created];
answer ← IF localStream = NIL THEN abort ELSE do;
matchFound ← TRUE;
};
ENDCASE => answer ← skip;
};
innerRetrieve: PROC [eachServer: ROPE] RETURNS [success: BOOLTRUE] = {
h ← NIL;
{
ENABLE UNWIND => ReturnConnection[eachServer, h];
h ← GetConnection[eachServer, file, FALSE];
STP.Retrieve[h, file, Confirm
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF NOT matchFound THEN {
IF timeSearch THEN {
file ← FSName.BangStarFile[file];
STP.Retrieve[h, file, Confirm];
};
IF NOT matchFound THEN {stpCode ← noSuchFile; success ← FALSE};
};
};
ReturnConnection[eachServer, h];
};
serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
FOR each: LIST OF ROPE ← serverList, each.rest WHILE each # NIL DO
IF innerRetrieve[each.first
! STP.Error => {
IF each = serverList THEN stpCode ← code;
IF each.rest = NIL THEN EXIT;
IF stpCode = noSuchFile AND replicated THEN EXIT;
LOOP}
] THEN RETURN ELSE IF replicated THEN EXIT;
ENDLOOP;
ReportSTPError[stpCode, server, file, wantedCreatedTime];
};
Store: PUBLIC PROC [server, file: ROPE, str: STREAM, created: GMT, proc: FSRemoteFile.ConfirmProc] = {
h: Handle ← NIL;
Note: STP.NoteFileProcType = {
[file: ROPE] RETURNS [continue: BOOL]
doIt: BOOL = proc [ FSName.VersionFromRope[ STP.GetFileInfo[h].version ] ];
continue ← IF doIt THEN yes ELSE no;
};
innerStore: PROC RETURNS [ok: BOOLTRUE] = {
tServer: ROPE ← FSPseudoServers.TranslateForWrite[server];
{
ENABLE UNWIND => ReturnConnection[tServer, h];
h ← GetConnection[tServer, file, TRUE];
STP.Store[h, file, str, Note, unknown, created];
};
ReturnConnection[tServer, h];
};
stpCode: STP.ErrorCode ← noSuchFile;
IF innerStore[ ! STP.Error => {stpCode ← code; CONTINUE}] THEN RETURN;
ReportSTPError[stpCode, server, file, BasicTime.nullGMT];
};
FTPTimeToGMT: PUBLIC PROC [t: Rope.ROPE] RETURNS [time: BasicTime.GMT ← BasicTime.nullGMT] = {
adjust: NAT ← 0;
Digit: PROC [index: INT] RETURNS [CARDINAL] = {
c: CHAR = Rope.Fetch[t, index-adjust];
SELECT c FROM
' => RETURN [0];
IN ['0..'9] => RETURN [c-'0];
ENDCASE => ERROR BadTime;
};
DoubleDigit: PROC [index: INT] RETURNS [CARDINAL] = {
RETURN [Digit[index]*10 + Digit[index+1]];
};
Char: PROC [index: INT] RETURNS [CHAR] = {
c: CHAR = Rope.Fetch[t, index];
SELECT c FROM
'-, '+, ' , IN ['a..'z], IN ['0..'9] => RETURN [c];
IN ['A..'Z] => RETURN [c + ('a-'A)];
ENDCASE => ERROR BadTime;
};
Inner: PROC = {
uT: BasicTime.Unpacked;
yr: CARDINAL = DoubleDigit[7];
SELECT yr FROM
< 50 => uT.year ← yr + 2000;
ENDCASE => uT.year ← yr + 1900;
IF Char[1] IN ['0..'9]
THEN uT.day ← DoubleDigit[0]
ELSE {
In this case we have a single-character day of month with no leading blank. We accept this number, as well as adjusting the indexing.
uT.day ← Digit[0];
adjust ← 1};
uT.day ← DoubleDigit[0];
uT.hour ← DoubleDigit[10];
uT.minute ← DoubleDigit[13];
uT.second ← DoubleDigit[16];
uT.month ← SELECT Char[3] FROM
'j => IF Char[4] = 'a THEN January ELSE IF Char[5] = 'n THEN June ELSE July,
'f => February,
'm => IF Char[5] = 'r THEN March ELSE May,
'a => IF Char[4] = 'p THEN April ELSE August,
's => September,
'o => October,
'n => November,
'd => December,
ENDCASE => ERROR BadTime;
IF t.Length[] < 19
THEN {
Yetch, FTPServer on AltoGateway does this. Use local zone info.
temp: BasicTime.Unpacked ← BasicTime.Unpack[BasicTime.Now[]];
uT.zone ← temp.zone;
uT.dst ← temp.dst;
}
ELSE {
uT.zone ← 60 * ( SELECT Char[19] FROM
'g => 0,
'e => 5,
'c => 6,
'm => 7,
'p => 8,
'+ => Digit[20],
'- => - Digit[20],
ENDCASE => ERROR BadTime) ;
uT.dst ← IF (Char[20] = 'd) THEN yes ELSE no;
};
time ← BasicTime.Pack[uT];
};
Inner[ ! RuntimeError.UNCAUGHT => CONTINUE];
};
BadTime: ERROR = CODE;
Internal procedures
STPInfo: PROC [server, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] = {
h: Handle ← NIL;
Note: STP.NoteFileProcType = {
[file: ROPE] RETURNS [continue: BOOL]
info: STP.FileInfo = STP.GetFileInfo[h];
created ← FTPTimeToGMT[info.create];
IF NOT timeSearch OR wantedCreatedTime = created
THEN {
version ← FSName.VersionFromRope[info.version];
bytes ← info.size;
matchFound ← TRUE;
If version part was missing from client's file name, then Enumerate will produce all versions, but we only want the !H version
continue ← IF timeSearch THEN no ELSE yes;
}
ELSE continue ← yes;
};
matchFound: BOOLFALSE;
timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT);
stpCode: STP.ErrorCode ← noSuchFile;
serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server];
replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server];
innerSTPInfo: PROC [eachServer: ROPE] RETURNS [found: BOOLTRUE] = {
ENABLE UNWIND => ReturnConnection[eachServer, h];
h ← NIL;
h ← GetConnection[eachServer, file, FALSE];
STP.Enumerate[h, file, Note
! STP.Error => IF code = noSuchFile THEN CONTINUE ];
IF NOT matchFound THEN {
IF timeSearch THEN {
file ← FSName.BangStarFile[file];
STP.Enumerate[h, file, Note];
};
IF NOT matchFound THEN {stpCode ← noSuchFile; found ← FALSE};
};
ReturnConnection[eachServer, h];
};
FOR each: LIST OF ROPE ← serverList, each.rest WHILE each # NIL DO
IF innerSTPInfo[each.first
! STP.Error => {
IF each = serverList THEN stpCode ← code;
IF each.rest = NIL THEN EXIT;
IF stpCode = noSuchFile AND replicated THEN EXIT;
LOOP};
] THEN RETURN ELSE IF replicated THEN EXIT;
ENDLOOP;
ReportSTPError[stpCode, server, file, wantedCreatedTime];
};
Property stuff
MyDesiredProperties: TYPE = PACKED ARRAY STP.ValidProperties OF BoolDefaultFalse;
BoolDefaultFalse: TYPE = BOOLFALSE;
namesOnly: MyDesiredProperties = [directory: TRUE, nameBody: TRUE, version: TRUE];
nameSizeCreated: MyDesiredProperties = [
directory: TRUE, nameBody: TRUE, version: TRUE, createDate: TRUE, size: TRUE];
all: STP.DesiredProperties = ALL[TRUE]; -- stops sending of Desired-Property properties
ConditionConnection: PROC[h: Handle, nameBody: ROPE, justNames: BOOL] = {
IF NOT Rope.Match["<*", nameBody]
THEN {
no directory, so turn off directory defaulting and Desired-Property properties
STP.SetDirectory[h, " "];
STP.SetDesiredProperties[h, all];
}
ELSE {
STP.SetDirectory[h, NIL];
STP.SetDesiredProperties[h, IF justNames THEN namesOnly ELSE nameSizeCreated];
};
};
Internal procedures
BracketServer: PROC[server: ROPE] RETURNS [ROPE] = {
IF Rope.Match["[*", server]
THEN RETURN [server]
ELSE RETURN [ Rope.Cat[ "[", server, "]" ] ];
};
UnbracketServer: PROC[server: ROPE] RETURNS [ROPE] = {
IF Rope.Match["[*", server]
THEN RETURN [ Rope.Flatten[server, 1, Rope.Length[server]-2]]
ELSE RETURN [ server ];
};
ReportSTPError: PUBLIC PROC [stpCode: STP.ErrorCode, server, file: ROPE, time: GMT] = {
gName: ROPE = Rope.Concat[BracketServer[server], file];
e1: ROPE ← "Server for \"";
e2: ROPE ← "\"";
code: FSBackdoor.ErrorCode;
NewError: PROC [group: FS.ErrorGroup, code: ATOM, explanation: ROPE] = {
ERROR FS.Error[[group, code, Rope.Cat[e1, gName, "\"", explanation]]];
};
IF stpCode = noSuchFile THEN {
FSReport.UnknownFile[gName, time]; -- raises FS.Error
};
SELECT stpCode FROM
noRouteToNetwork, noNameLookupResponse => {
code ← serverInaccessible;
e2 ← "\" is inaccessible";
};
connectionClosed => {
code ← wentOffline;
e2 ← "\" connection closed unexpectedly (wentOffline)";
};
connectionRejected => {
code ← connectionRejected;
e2 ← "\" rejected the connection attempt";
};
connectionTimedOut => {
code ← connectionTimedOut;
e2 ← "\" timed-out the connection";
};
accessDenied => {
code ← accessDenied;
e2 ← "\" denied file access permission";
};
requestRefused => {
code ← quotaExceeded;
e1 ← "Request refused (possibily no quota for storing) for \"";
};
accessError => {
code ← fileBusy;
e1 ← "\"";
e2 ← "\" is locked on the server";
};
illegalUserName => {
code ← badCredentials;
e1 ← "Credentials rejected when accessing \"";
};
illegalFileName => {
code ← illegalName;
e2 ← "\" says that the file name is illegal";
};
noSuchHost => {
code ← unknownServer;
e1 ← "Couldn't find the server for \"";
};
alreadyAConnection =>
NewError[bug, $alreadyAConnection, " already had a connection"];
noConnection =>
NewError[bug, $noConnection, " gave a noConnection error"];
illegalUserPassword =>
NewError[environment, $illegalUserPassword, " had an illegal user password"];
illegalUserAccount =>
NewError[environment, $illegalUserAccount, " had an illegal user account"];
illegalConnectName =>
NewError[environment, $illegalConnectName, " had an illegal connect name"];
illegalConnectPassword =>
NewError[environment, $illegalConnectPassword, " had an illegal connect password"];
credentailsMissing =>
NewError[environment, $credentailsMissing, " had missing credentails"];
protocolError =>
NewError[bug, $protocolError, " gave a protocol error to STP"];
noSuchFile =>
NewError[bug, $noSuchFile, " reported no such file"];
undefinedError =>
NewError[bug, $undefinedError, " gave STP an undefinedError"];
ENDCASE => ERROR;
FSBackdoor.ProduceError[code, Rope.Cat[e1, gName, e2]];
};
STP connection cacheing
maxSlots: CARDINAL = 9; -- only need enough for all used in last TimeOut seconds
TimeOut: Process.Seconds ← 7; -- keep connection around for this long after use
SlotArray: TYPE = ARRAY [0..maxSlots) OF Slot;
Slot: TYPE = RECORD [
server: ROPE,
h: Handle,
used: BOOL
];
emptySlot: Slot = [NIL, NIL, FALSE];
new, reused, flushed: INT ← 0; -- statistics
slot: REF SlotArray ← NEW[SlotArray ← ALL [emptySlot]];
haveSlotTimer: BOOLFALSE;
ForTimeout: CONDITION ← [Process.SecondsToTicks[TimeOut]];
GrapevineCacheArray: TYPE = ARRAY [0..maxGVineCache) OF GrapevineCacheEntry;
GrapevineCacheEntry: TYPE = RECORD[
name: ROPENIL,
connect: GVBasics.Connect
];
maxGVineCache: CARDINAL = 9; -- its quite expensive to find these out
grapevineCachePut: CARDINAL ← 0; -- next victim
grapevineCache: REF GrapevineCacheArray ← NEW[GrapevineCacheArray];
GetConnection: PROC [server, pattern: ROPE, justNames: BOOL] RETURNS [h: Handle] = {
h ← LookForConnection[server];
IF h = NIL
THEN {
need a new connection
user, password: ROPE;
h ← STP.Create[];
[user, password] ← UserCredentials.Get[];
STP.Login[h, user, password];
[] ← STP.Open[h, GetServerPupName[server]];
}
ELSE {
already had a connection
IF NOT STP.IsOpen[h] THEN {
[] ← STP.Open[h, GetServerPupName[server]];
};
};
ConditionConnection[h, pattern, justNames];
};
GetServerPupName: PUBLIC PROC [server: ROPE] RETURNS [pupServer: ROPE] = {
IF server.Find[".", 0, FALSE] > 0 THEN {
Names with "." are GVNames (Grapevine names), so ask Grapevine to look them up
info: GVNames.ConnectInfo;
connect: GVBasics.Connect;
foundInCache: BOOL;
[found: foundInCache, connect: connect] ← FindInGrapevineCache[name: server];
IF foundInCache THEN RETURN[connect];
[info: info, connect: connect ] ← GVNames.GetConnect[server];
If successful, use the connect as the server name for STP.Open
IF info = group OR info = individual THEN {
AddToGrapevineCache[name: server, connect: connect];
RETURN[connect];
};
};
RETURN[server];
};
FindInGrapevineCache: ENTRY PROC [name: ROPE] RETURNS [found: BOOLFALSE, connect: GVBasics.Connect] = {
FOR i: CARDINAL IN [0..maxGVineCache) DO
IF Rope.Equal[name, grapevineCache[i].name, FALSE] THEN RETURN[TRUE, grapevineCache[i].connect];
ENDLOOP;
};
AddToGrapevineCache: ENTRY PROC [name: ROPE, connect: GVBasics.Connect] = {
grapevineCache[grapevineCachePut].name ← name;
grapevineCache[grapevineCachePut].connect ← connect;
IF (grapevineCachePut ← grapevineCachePut + 1) >= maxGVineCache THEN grapevineCachePut ← 0;
};
LookForConnection: ENTRY PROC[server: ROPE] RETURNS [h: Handle] = {
ENABLE UNWIND => NULL;
FOR i: CARDINAL IN [0..maxSlots) DO
IF slot[i] # emptySlot AND Rope.Equal[slot[i].server, server, FALSE] THEN {
reused ← reused + 1;
h ← slot[i].h;
slot[i] ← emptySlot;
RETURN;
};
ENDLOOP;
h ← NIL;
new ← new + 1;
};
ReturnConnection: PROC [server: ROPE, h: Handle] = {
IF server = NIL OR h = NIL THEN RETURN;
IF STP.IsOpen[h] AND NOT SaveConnection[server, h] THEN
STP.Close[h ! STP.Error => CONTINUE ];
};
SaveConnection: ENTRY PROC [server: ROPE, h: Handle] RETURNS [BOOL] = {
ENABLE UNWIND => NULL;
FOR i: CARDINAL IN [0..maxSlots) DO
IF slot[i] = emptySlot THEN {
slot[i] ← [server, h, TRUE];
IF NOT haveSlotTimer THEN TRUSTED {
haveSlotTimer ← TRUE;
Process.Detach[FORK SlotTimer[]];
};
RETURN [TRUE];
};
ENDLOOP;
RETURN [FALSE];
};
SlotTimer: PROC = {
i: CARDINAL ← 0;
h: Handle;
DO
[h, i] ← NextInterestingSlot[i];
IF h = NIL THEN RETURN;
STP.Close[h ! STP.Error => LOOP ];
flushed ← flushed + 1;
ENDLOOP;
};
NextInterestingSlot: ENTRY PROC [start: CARDINAL] RETURNS [h: Handle, index: CARDINAL] = {
ENABLE UNWIND => NULL;
DO
IF start = 0 THEN WAIT ForTimeout;
FOR index IN [start..maxSlots) DO
IF slot[index] # emptySlot THEN {
IF slot[index].used
THEN slot[index].used ← FALSE
ELSE { -- here's one that needs to be closed
h ← slot[index].h;
slot[index] ← emptySlot;
RETURN;
};
};
ENDLOOP;
FOR index IN [0 .. maxSlots) DO
IF slot[index] # emptySlot THEN EXIT; -- need to stick around
REPEAT FINISHED => { -- nothing more to do
h ← NIL;
index ← maxSlots;
haveSlotTimer ← FALSE;
RETURN;
};
ENDLOOP;
start ← 0;
ENDLOOP;
};
}.
Bob Hagmann April 29, 1985 9:12:49 am PDT
changes to: DIRECTORY, GetConnection
Bob Hagmann April 29, 1985 10:03:03 am PDT
changes to: DIRECTORY, GetConnection, GetServerPupName, LookForConnection, FSRemoteFileImpl
Bob Hagmann May 6, 1985 4:13:27 pm PDT
changes to: InnerEnumerate
Bob Hagmann August 2, 1985 3:25:38 pm PDT
changes to: maxSlots, TimeOut, GetServerPupName, FindInGrapevineCache, AddToGrapevineCache, LookForConnection, ForTimeout, GrapevineCacheEntry, maxGVineCache, grapevineCachePut, grapevineCache, GetConnection
Bob Hagmann August 2, 1985 4:37:37 pm PDT
changes to: grapevineCachePut