-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- PupDirServer.mesa, HGM, 5-Nov-85 19:38:45
DIRECTORY
Ascii USING [CR],
CmFile USING [Handle, TableError],
Event USING [aboutToSwap],
EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
Heap USING [systemZone],
MFile USING [AddNotifyProc, Handle, RemoveNotifyProc],
Process USING [Detach, SetTimeout, MsecToTicks, Pause],
Put USING [Text],
String USING [AppendChar, AppendString, AppendDecimal],
StringLookUp USING [noMatch],
Stream USING [Handle],
Supervisor USING [
AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
SubsystemHandle],
Token USING [FreeTokenString, Item],
Time USING [AppendCurrent],
Window USING [Handle],
Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
Indirect USING [Close, GetParmFileName, NextValue, OpenSection],
MiscServerDefs USING [
PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetDirectoryServer],
NameServerDefs USING [
lockDirRequest, lockDirReply, unlockDirRequest, unlockDirReply,
CloseDirectoryFiles, CreateTempFile, DeleteTempFile, FlushWholeCache,
GetNewDirectoryVersion, GetOldDirectoryVersion,
GetStreamForNewDirectoryFile, GetStreamForOldDirectoryFile,
MakeTempFileIntoNewDirectoryFile, MakeTempFileIntoOldDirectoryFile,
OpenDirectoryFiles],
PupDefs USING [
AppendHostName, GetPupAddress, GetPupContentsBytes, PupAddress, PupBuffer,
PupNameTrouble, PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketID,
PupSocketMake, SecondsToTocks, SetPupContentsWords,
ReturnPup, SendPup, UniqueLocalPupAddress, UniqueLocalPupSocketID],
PupTypes USING [fillInPupAddress, miscSrvSoc],
Slosh USING [RecvStatus, RecvFile, RetransmissionInterval, SendFile],
Stats USING [StatCounterIndex, StatIncr];
PupDirServer: MONITOR
IMPORTS
CmFile, Event, Heap, Indirect, MFile, Process, Put,
String, Supervisor, Time, Token, MiscServerDefs, NameServerDefs,
Buffer, PupDefs, Slosh, Stats
EXPORTS NameServerDefs =
BEGIN OPEN Stats, PupDefs;
msg: PUBLIC Window.Handle ← NIL;
broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];
z: UNCOUNTED ZONE = Heap.systemZone;
remote: Chain ← NIL;
Chain: TYPE = LONG POINTER TO ChainSlot;
ChainSlot: TYPE = RECORD [
next: Chain,
name: LONG STRING];
dirRunning, probing, fetching, sending: PUBLIC BOOLEAN ← FALSE;
useCount: CARDINAL ← 0;
pleaseStop: BOOLEAN ← FALSE;
probePeriod: CARDINAL ← 60; -- in minutes
tries: CARDINAL ← 0;
delay: CONDITION;
lock: BOOLEAN ← FALSE;
verbose: BOOLEAN = TRUE;
oldFileName: STRING = "Pup-network.directory";
newFileName: STRING = "Pup-network.big";
parmFileName: LONG STRING ← Indirect.GetParmFileName[];
statVers, statSend: PUBLIC StatCounterIndex;
PupDirServerOn: PUBLIC ENTRY PROCEDURE =
BEGIN
IF (useCount ← useCount + 1) = 1 THEN
BEGIN
Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
dirRunning ← TRUE;
Starter[];
END;
--UpdatePicture[];
END;
Starter: PROCEDURE =
BEGIN
pleaseStop ← FALSE;
MiscServerDefs.PupMiscServerOn[];
NameServerDefs.OpenDirectoryFiles[];
ScanParameterFile[];
MiscServerDefs.SetDirectoryServer[PupDirServer];
Process.Detach[FORK ProbeGovenor[]];
END;
PupDirServerOff: PUBLIC ENTRY PROCEDURE =
BEGIN
IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
BEGIN
dirRunning ← FALSE;
Stopper[];
Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
MFile.RemoveNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
END;
--UpdatePicture[];
END;
Stopper: INTERNAL PROCEDURE =
BEGIN
oneTick: CONDITION;
Process.SetTimeout[@oneTick, 1];
MiscServerDefs.SetDirectoryServer[MiscServerDefs.IgnoreThisPacket];
pleaseStop ← TRUE;
NOTIFY delay;
WHILE probing OR fetching OR sending DO WAIT oneTick; ENDLOOP;
NameServerDefs.CloseDirectoryFiles[];
ForgetParameters[];
MiscServerDefs.PupMiscServerOff[];
END;
CountTries: ENTRY PROCEDURE = BEGIN tries ← tries + 1; END;
KickProber: PUBLIC ENTRY PROCEDURE =
BEGIN IF tries > 3 THEN RETURN; StartProbing[]; END;
StartProbingForDirectory: PUBLIC ENTRY PROCEDURE = BEGIN StartProbing[]; END;
StartProbing: INTERNAL PROCEDURE =
BEGIN
IF pleaseStop OR probing OR fetching THEN RETURN;
probing ← TRUE;
Process.Detach[FORK Probe[]];
END;
ProbeGovenor: ENTRY PROCEDURE =
BEGIN
n: CARDINAL ← 1; -- probe 1 min after startup
Process.SetTimeout[@delay, Process.MsecToTicks[60000]];
UNTIL pleaseStop DO
WAIT delay; -- one minute
IF probing OR fetching THEN BEGIN n ← probePeriod; LOOP; END;
IF (n ← n - 1) = 0 THEN
BEGIN
tries ← 0;
StartProbing[];
n ← probePeriod;
END;
ENDLOOP;
END;
Probe: PROCEDURE =
BEGIN
b: PupBuffer;
from: PupSocketID ← UniqueLocalPupSocketID[];
pool: Buffer.AccessHandle;
soc: PupSocket;
sloshing, sloshingNew, sloshingOld: BOOLEAN;
currentVersionNew, currentVersionOld: CARDINAL;
[currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
[currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
sloshing ← sloshingOld OR sloshingNew;
IF sloshing OR fetching THEN BEGIN probing ← FALSE; RETURN; END;
pool ← Buffer.MakePool[send: 1, receive: 10];
soc ← PupSocketMake[from, PupTypes.fillInPupAddress, SecondsToTocks[1]];
THROUGH [0..5) UNTIL sloshing DO
b ← Buffer.GetBuffer[pup, pool, send];
b.pup.source.socket ← from;
b.pup.dest.socket ← PupTypes.miscSrvSoc;
b.pup.pupType ← netDirVersion;
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
SetPupContentsWords[b, 2];
PupRouterBroadcastThis[b];
UNTIL pleaseStop OR sloshing DO
b ← soc.get[];
IF b = NIL THEN EXIT;
IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
sloshing ← LookAtHisVersion[b];
ENDLOOP;
ENDLOOP;
PupSocketDestroy[soc];
Buffer.DestroyPool[pool];
FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
ProbeRemote[finger.name];
ENDLOOP;
probing ← FALSE;
END;
ProbeRemote: PROCEDURE [remote: LONG STRING] =
BEGIN
b: PupBuffer;
from: PupSocketID ← UniqueLocalPupSocketID[];
pool: Buffer.AccessHandle;
soc: PupSocket;
sloshing, sloshingNew, sloshingOld: BOOLEAN;
currentVersionNew, currentVersionOld: CARDINAL;
targetInvalid: BOOLEAN ← FALSE;
target: PupAddress ← [[0], [0], PupTypes.miscSrvSoc];
[currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
[currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
sloshing ← sloshingOld OR sloshingNew;
IF sloshing THEN RETURN;
PupDefs.GetPupAddress[
@target, remote ! PupDefs.PupNameTrouble => BEGIN targetInvalid ← TRUE; CONTINUE; END];
IF targetInvalid THEN RETURN;
pool ← Buffer.MakePool[send: 1, receive: 10];
soc ← PupSocketMake[from, target, SecondsToTocks[5]];
THROUGH [0..5) UNTIL sloshing DO
b ← Buffer.GetBuffer[pup, pool, send];
b.pup.source.socket ← from;
b.pup.dest ← target;
b.pup.pupType ← netDirVersion;
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
SetPupContentsWords[b, 2];
soc.put[b];
UNTIL pleaseStop OR sloshing DO
b ← soc.get[];
IF b = NIL THEN EXIT;
IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
sloshing ← LookAtHisVersion[b];
ENDLOOP;
ENDLOOP;
PupSocketDestroy[soc];
Buffer.DestroyPool[pool];
END;
LookAtHisVersion: PROCEDURE [b: PupDefs.PupBuffer] RETURNS [sloshing: BOOLEAN] =
BEGIN
sloshingNew, sloshingOld: BOOLEAN;
currentVersionNew, currentVersionOld: CARDINAL;
hisVersionNew, hisVersionOld: CARDINAL;
[currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
[currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
sloshing ← sloshingOld OR sloshingNew;
IF sloshing THEN BEGIN Buffer.ReturnBuffer[b]; RETURN; END;
IF GetPupContentsBytes[b] > 2 THEN
BEGIN
hisVersionNew ← b.pup.pupWords[1];
SELECT hisVersionNew FROM
= currentVersionNew => NULL; --NB: Check first or LOOP talking to self on startup
= 0 =>
BEGIN
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
ReturnPup[b, netDirVersion, 2*2];
RETURN;
END;
> currentVersionNew =>
BEGIN
IF ~SetFetching[] THEN
BEGIN
where: PupAddress ← b.pup.source;
IF verbose THEN
BEGIN OPEN String;
text: STRING = [100];
Time.AppendCurrent[text];
AppendString[text, " Found "L];
AppendString[text, newFileName];
AppendString[text, "!"L];
AppendDecimal[text, hisVersionNew];
AppendString[text, " on "L];
AppendHostName[text, where];
LogString[text];
END;
Process.Detach[FORK FetchNewDirectory[where]];
END;
sloshing ← TRUE;
END;
< currentVersionNew => -- tell him about our newer one
BEGIN
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
ReturnPup[b, netDirVersion, 2*2];
RETURN;
END;
ENDCASE => ERROR;
END;
hisVersionOld ← b.pup.pupWords[0];
SELECT hisVersionOld FROM
= currentVersionOld => NULL; --NB: Check first or LOOP talking to self on startup
= 0 =>
BEGIN
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
ReturnPup[b, netDirVersion, 2*2];
RETURN;
END;
> currentVersionOld =>
BEGIN
IF ~SetFetching[] THEN
BEGIN
where: PupAddress ← b.pup.source;
IF verbose THEN
BEGIN OPEN String;
text: STRING = [100];
Time.AppendCurrent[text];
AppendString[text, " Found "L];
AppendString[text, oldFileName];
AppendString[text, "!"L];
AppendDecimal[text, hisVersionOld];
AppendString[text, " on "L];
AppendHostName[text, where];
LogString[text];
END;
Process.Detach[FORK FetchOldDirectory[where]];
END;
sloshing ← TRUE;
END;
< currentVersionOld => -- tell him about our newer one
BEGIN
b.pup.pupWords[0] ← currentVersionOld;
b.pup.pupWords[1] ← currentVersionNew;
ReturnPup[b, netDirVersion, 2*2];
RETURN;
END;
ENDCASE => ERROR;
Buffer.ReturnBuffer[b];
END;
SetFetching: ENTRY PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF fetching THEN RETURN[TRUE];
fetching ← TRUE;
RETURN[FALSE];
END;
FetchOldDirectory: PROCEDURE [where: PupAddress] =
BEGIN
pool: Buffer.AccessHandle;
from: PupAddress ← UniqueLocalPupAddress[@where];
fh: MFile.Handle;
sh: Stream.Handle;
status: Slosh.RecvStatus;
AskOld: PROCEDURE =
BEGIN
b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
b.pup.source ← from;
b.pup.dest ← where;
b.pup.address ← from;
SendPup[b, sendNetDir, 2*SIZE[PupAddress]];
END;
where.socket ← PupTypes.miscSrvSoc;
CountTries[];
[fh, sh] ← NameServerDefs.CreateTempFile[];
pool ← Buffer.MakePool[send: 1, receive: 0];
status ← Slosh.RecvFile[msg, oldFileName, sh, from, AskOld];
Buffer.DestroyPool[pool];
IF status = statusStoreOk THEN
BEGIN
IF NameServerDefs.MakeTempFileIntoOldDirectoryFile[fh] THEN
BEGIN -- Wait for MFile to tell us about the new file
sloshing: BOOLEAN;
currentVersion: CARDINAL;
DO
[currentVersion, sloshing] ← NameServerDefs.GetOldDirectoryVersion[];
IF ~sloshing AND currentVersion # 0 THEN EXIT;
Process.Pause[Process.MsecToTicks[1000]];
ENDLOOP;
END
ELSE
BEGIN
text: STRING = [100];
String.AppendString[text, "Oops, after all that, it didn't work"L];
LogString[text];
END;
END
ELSE
BEGIN
n: CARDINAL ← Slosh.RetransmissionInterval[];
NameServerDefs.DeleteTempFile[fh];
IF status = statusDiskFull THEN {
CountTries[]; CountTries[]; CountTries[]; };
THROUGH [0..n) UNTIL pleaseStop DO
Process.Pause[Process.MsecToTicks[1000]];
ENDLOOP;
END;
fetching ← FALSE;
KickProber[]; -- Try again or tell others
END;
FetchNewDirectory: PROCEDURE [where: PupAddress] =
BEGIN
pool: Buffer.AccessHandle;
from: PupAddress ← UniqueLocalPupAddress[@where];
fh: MFile.Handle;
sh: Stream.Handle;
status: Slosh.RecvStatus;
AskNew: PROCEDURE =
BEGIN
b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
b.pup.source ← from;
b.pup.dest ← where;
b.pup.address ← from;
SendPup[b, sendNetDir, 2*SIZE[PupAddress]+1]; -- Garbage byte is new marker
END;
where.socket ← PupTypes.miscSrvSoc;
CountTries[];
[fh, sh] ← NameServerDefs.CreateTempFile[];
pool ← Buffer.MakePool[send: 1, receive: 0];
status ← Slosh.RecvFile[msg, newFileName, sh, from, AskNew];
Buffer.DestroyPool[pool];
IF status = statusStoreOk THEN
BEGIN
IF NameServerDefs.MakeTempFileIntoNewDirectoryFile[fh] THEN
BEGIN -- Wait for MFile to tell us about the new file
sloshing: BOOLEAN;
currentVersion: CARDINAL;
DO
[currentVersion, sloshing] ← NameServerDefs.GetNewDirectoryVersion[];
IF ~sloshing AND currentVersion # 0 THEN EXIT;
Process.Pause[Process.MsecToTicks[1000]];
ENDLOOP;
END
ELSE
BEGIN
text: STRING = [100];
String.AppendString[text, "Oops, after all that, it didn't work"L];
LogString[text];
END;
END
ELSE
BEGIN
n: CARDINAL ← Slosh.RetransmissionInterval[];
NameServerDefs.DeleteTempFile[fh];
IF status = statusDiskFull THEN {
CountTries[]; CountTries[]; CountTries[]; };
THROUGH [0..n) UNTIL pleaseStop DO
Process.Pause[Process.MsecToTicks[1000]];
ENDLOOP;
END;
fetching ← FALSE;
KickProber[]; -- Try again or tell others
END;
SendOldDirectory: PROCEDURE [where: PupAddress] =
BEGIN
stream: Stream.Handle;
IF verbose THEN
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, oldFileName];
String.AppendString[text, " wanted by "L];
AppendHostName[text, where];
LogString[text];
END;
stream ← NameServerDefs.GetStreamForOldDirectoryFile[];
IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END;
IF Slosh.SendFile[msg, oldFileName, stream, where] = ok THEN StatIncr[statSend];
sending ← FALSE;
KickProber[]; -- tell others
END;
SendNewDirectory: PROCEDURE [where: PupAddress] =
BEGIN
stream: Stream.Handle;
IF verbose THEN
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, newFileName];
String.AppendString[text, " wanted by "L];
AppendHostName[text, where];
LogString[text];
END;
stream ← NameServerDefs.GetStreamForNewDirectoryFile[];
IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END;
IF Slosh.SendFile[msg, newFileName, stream, where] = ok THEN StatIncr[statSend];
sending ← FALSE;
KickProber[]; -- tell others
END;
PupDirServer: PUBLIC PROCEDURE [b: PupBuffer] =
BEGIN
IF ~(lock OR pleaseStop) OR b.pup.pupType = NameServerDefs.unlockDirRequest THEN
SELECT b.pup.pupType FROM
netDirVersion =>
BEGIN
StatIncr[statVers];
[] ← LookAtHisVersion[b];
RETURN;
END;
sendNetDir =>
BEGIN
IF ~sending THEN
BEGIN
sending ← TRUE;
-- An extra garbage byte is the marker to request the new file format
IF GetPupContentsBytes[b] > 2*SIZE[PupAddress] THEN
Process.Detach[FORK SendNewDirectory[b.pup.address]]
ELSE Process.Detach[FORK SendOldDirectory[b.pup.address]];
END;
END;
NameServerDefs.lockDirRequest =>
BEGIN
lock ← TRUE;
ReturnPup[b, NameServerDefs.lockDirReply, 0];
NameServerDefs.CloseDirectoryFiles[];
NameServerDefs.FlushWholeCache[];
RETURN;
END;
NameServerDefs.unlockDirRequest =>
BEGIN
wasLocked: BOOLEAN ← lock;
lock ← FALSE;
ReturnPup[b, NameServerDefs.unlockDirReply, 0];
IF wasLocked THEN NameServerDefs.OpenDirectoryFiles[];
tries ← 0;
KickProber[];
RETURN;
END;
ENDCASE;
Buffer.ReturnBuffer[b];
END;
ScanParameterFile: PROCEDURE =
BEGIN
cmFile: CmFile.Handle;
Option: TYPE = {remote};
NextValue: PROCEDURE [
h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
optionTable: ARRAY Option OF LONG STRING ← [remote: "Remote"L];
cmFile ← Indirect.OpenSection["DirectoryServer"L];
IF cmFile = NIL THEN RETURN;
DO
option: Option;
text: STRING = [200];
option ← NextValue[
cmFile, DESCRIPTOR[optionTable] !
CmFile.TableError =>
BEGIN
IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
RETRY;
END];
SELECT option FROM
LOOPHOLE[StringLookUp.noMatch] => EXIT;
remote =>
BEGIN
temp: LONG STRING ← Token.Item[cmFile, FALSE];
new: Chain ← z.NEW[ChainSlot];
new↑ ← [NIL, z.NEW[StringBody[temp.length]]];
String.AppendString[new.name, temp];
[] ← Token.FreeTokenString[temp];
IF remote = NIL THEN remote ← new
ELSE
BEGIN
FOR finger: Chain ← remote, finger.next DO
IF finger.next = NIL THEN
BEGIN
finger.next ← new;
EXIT;
END;
ENDLOOP;
END;
Message["Watching for new versions of Pup-network.* on "L, new.name];
END;
ENDCASE => ERROR;
ENDLOOP;
Indirect.Close[cmFile];
END;
ForgetParameters: PROCEDURE =
BEGIN
UNTIL remote = NIL DO
temp: Chain ← remote;
remote ← remote.next;
z.FREE[@temp.name];
z.FREE[@temp];
ENDLOOP;
END;
Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
BEGIN
text: STRING = [200];
String.AppendString[text, "DirServer: "L];
String.AppendString[text, one];
IF two # NIL THEN String.AppendString[text, two];
IF three # NIL THEN String.AppendString[text, three];
IF four # NIL THEN String.AppendString[text, four];
LogString[text];
END;
LogString: PROCEDURE [text: LONG STRING] =
BEGIN
String.AppendChar[text, '.];
String.AppendChar[text, Ascii.CR];
Put.Text[NIL, text];
IF msg # NIL THEN Put.Text[msg, text];
END;
Inspect: ENTRY PROCEDURE [
name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
RETURNS [BOOLEAN] =
BEGIN
IF parmFileName = NIL THEN RETURN[FALSE];
Message["Recycling because a new version of "L, parmFileName, " arrived"L];
Stopper[];
Starter[];
RETURN[FALSE]
END;
Broom: ENTRY Supervisor.AgentProcedure =
BEGIN
SELECT event FROM
EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume =>
IF dirRunning THEN Stopper[];
ENDCASE => NULL;
END;
END.