-- File: PupDirServerNoDisk.mesa - last edit:
-- WIrish 7-Feb-88 19:29:02
-- AOF 3-Feb-88 14:32:58
-- HGM 9-May-86 16:14:21
-- Copyright (C) 1984, 1988 by Xerox Corporation. All rights reserved.
-- We don't have a copy of the file, but we keep a BIG cache of things we find
-- on other servers so we can pass the service on to clients on adjacent nets.
-- This module watches the directory version on the net (and on the NameServer
-- Helpers), and flushes the cache so it will get reloaded with current information.
-- It also "forwards" netDirVersion requests aka the Pup boot servers to keep IFSs happy.
DIRECTORY
Ascii USING [CR],
CmFile USING [Handle, TableError],
Heap USING [systemZone],
Process USING [Detach, SetTimeout, MsecToTicks],
Put USING [Text],
String USING [AppendChar, AppendNumber, AppendString, AppendDecimal],
StringLookUp USING [noMatch],
Time USING [AppendCurrent],
Token USING [FreeTokenString, Item, Octal],
Indirect USING [Close, NextValue, OpenSection],
MiscServerDefs USING [PupMiscServerOn, SetDirectoryServer],
NameServerDefs USING [
lockDirRequest, lockDirReply, unlockDirRequest, unlockDirReply,
FlushWholeCache, PupNameServerOn, BumpCacheSize],
PupDefs USING [
AppendHostName, Body, GetPupAddress, GetPupContentsBytes, PupBuffer, PupNameTrouble,
PupRouterBroadcastThis, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketID,
PupSocketMake, SecondsToTocks, SetPupContentsWords,
ReturnPup, UniqueLocalPupSocketID,
AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
PupRouterDefs USING [NetworkContext],
PupTypes USING [fillInPupAddress, miscSrvSoc, PupAddress, PupNetID],
Stats USING [StatCounterIndex, StatIncr];
PupDirServerNoDisk: MONITOR
IMPORTS
CmFile, Heap, Process, Put, String, Time, Token,
Indirect, MiscServerDefs, NameServerDefs, PupDefs, Stats
EXPORTS NameServerDefs =
BEGIN OPEN Stats, NameServerDefs, PupDefs;
z: UNCOUNTED ZONE = Heap.systemZone;
remote: Chain ← NIL;
helpers: HelperChain ← NIL;
Chain: TYPE = LONG POINTER TO ChainSlot;
ChainSlot: TYPE = RECORD [
next: Chain,
source: PupTypes.PupNetID,
dest: PupTypes.PupAddress,
target: LONG STRING];
HelperChain: TYPE = LONG POINTER TO HelperChainSlot;
HelperChainSlot: TYPE = RECORD [
next: HelperChain,
dest: PupTypes.PupAddress,
target: LONG STRING];
dirRunning, probing, sending: PUBLIC BOOLEAN ← FALSE;
useCount: CARDINAL ← 0;
pleaseStop: BOOLEAN ← FALSE;
probePeriod: CARDINAL ← 30; -- in minutes
tries: CARDINAL ← 0;
delay: CONDITION;
lock: BOOLEAN ← FALSE;
verbose: BOOLEAN = TRUE;
currentVersion: CARDINAL ← 0;
oldVersionAround: BOOLEAN ← TRUE;
fileName: STRING = "Pup-network.directory";
statVers, statSend: PUBLIC StatCounterIndex;
GetNewDirectoryVersion, GetOldDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL, BOOLEAN] =
BEGIN RETURN[currentVersion, FALSE]; END;
PupDirServerOn: PUBLIC PROCEDURE =
BEGIN
IF (useCount ← useCount + 1) = 1 THEN
BEGIN
dirRunning ← TRUE;
Starter[];
END;
END;
Starter: PROCEDURE =
BEGIN
pleaseStop ← FALSE;
MiscServerDefs.PupMiscServerOn[];
ScanParameterFile[];
MiscServerDefs.SetDirectoryServer[PupDirServer];
Process.Detach[FORK ProbeGovenor[]];
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 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 THEN BEGIN n ← probePeriod; LOOP; END;
IF (n ← n - 1) = 0 THEN
BEGIN
tries ← 0;
StartProbing[];
WHILE probing DO WAIT delay; ENDLOOP;
n ← probePeriod;
END;
ENDLOOP;
END;
Probe: PROCEDURE =
BEGIN
b: PupBuffer;
from: PupSocketID ← UniqueLocalPupSocketID[];
pool: PupDefs.AccessHandle ← PupDefs.MakePool[send: 1, receive: 10];
soc: PupSocket;
bestSoFar: CARDINAL ← currentVersion;
where: PupTypes.PupAddress;
who: PupTypes.PupAddress ← PupTypes.fillInPupAddress;
sawOldVersion: BOOLEAN ← FALSE;
body: PupDefs.Body;
helper: HelperChain ← helpers;
tryBroadcast: BOOLEAN ← FALSE;
try: CARDINAL ← 2;
-- Check the NameServer Helpers first, then try local broadcasts...
soc ← PupSocketMake[from, who, SecondsToTocks[5]];
IF helper # NIL
THEN who ← helper.dest
ELSE tryBroadcast ← TRUE;
UNTIL (bestSoFar > currentVersion) OR (tryBroadcast AND try = 0) DO
IF try = 0 THEN
BEGIN
IF helper # NIL THEN helper ← helper.next;
IF helper # NIL
THEN who ← helper.dest
ELSE tryBroadcast ← TRUE;
try ← 2;
END;
try ← try - 1;
b ← PupDefs.GetBuffer[pool, send];
body ← b.pup;
body.dest ← who;
body.source ← soc.getLocalAddress[];
body.source.socket ← from;
body.dest.socket ← PupTypes.miscSrvSoc;
body.pupType ← netDirVersion;
body.pupWords[0] ← 0;
body.pupWords[1] ← 0;
SetPupContentsWords[b, 2];
IF tryBroadcast
THEN PupRouterBroadcastThis[b]
ELSE PupRouterSendThis[b];
DO
b ← soc.get[];
IF b = NIL THEN EXIT;
IF b.pup.pupType = netDirVersion THEN
BEGIN
SELECT b.pup.pupWords[0] FROM
> bestSoFar =>
BEGIN bestSoFar ← b.pup.pupWords[0]; where ← b.pup.source; END;
< currentVersion => sawOldVersion ← TRUE;
ENDCASE => NULL;
IF GetPupContentsBytes[b] > 2 THEN
BEGIN
SELECT b.pup.pupWords[1] FROM
> bestSoFar =>
BEGIN bestSoFar ← b.pup.pupWords[0]; where ← b.pup.source; END;
< currentVersion => sawOldVersion ← TRUE;
ENDCASE => NULL;
END;
END;
PupDefs.ReturnBuffer[b];
ENDLOOP;
ENDLOOP;
PupSocketDestroy[soc];
PupDefs.DestroyPool[pool];
IF bestSoFar > currentVersion THEN
BEGIN
IF verbose THEN
BEGIN OPEN String;
text: STRING = [100];
Time.AppendCurrent[text];
AppendString[text, " Saw "L];
AppendString[text, fileName];
AppendString[text, "!"L];
AppendDecimal[text, bestSoFar];
AppendString[text, " on "L];
AppendHostName[text, where];
LogString[text];
END;
FlushWholeCache[];
currentVersion ← bestSoFar;
probing ← FALSE;
RETURN;
END;
IF sawOldVersion OR oldVersionAround THEN
BEGIN
oldVersionAround ← sawOldVersion;
FlushWholeCache[];
END;
probing ← FALSE;
END;
PupDirServer: PUBLIC PROCEDURE [b: PupBuffer] =
BEGIN
IF ~(lock OR pleaseStop) OR b.pup.pupType = unlockDirRequest THEN
SELECT b.pup.pupType FROM
netDirVersion =>
BEGIN
StatIncr[statVers];
SELECT b.pup.pupWords[0] FROM
= currentVersion => NULL; -- we have the same ones
> currentVersion => KickProber[]; -- he has a newer one
< currentVersion => NULL; -- Don't respond since we can't send it
ENDCASE => ERROR;
MaybeForwardThisOne[b];
RETURN;
END;
sendNetDir => NULL;
lockDirRequest =>
BEGIN
lock ← TRUE;
ReturnPup[b, lockDirReply, 0];
FlushWholeCache[];
RETURN;
END;
unlockDirRequest =>
BEGIN
wasLocked: BOOLEAN ← lock;
lock ← FALSE;
ReturnPup[b, unlockDirReply, 0];
tries ← 0;
KickProber[];
RETURN;
END;
ENDCASE;
PupDefs.ReturnBuffer[b];
END;
MaybeForwardThisOne: PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN
context: PupRouterDefs.NetworkContext = b.fo.context;
IF b.pup.source.net = 0 THEN b.pup.source.net ← [context.pupNetNumber];
FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
IF context.pupNetNumber = finger.source THEN
BEGIN
IF finger.dest = PupTypes.fillInPupAddress THEN EXIT;
b.pup.dest.net ← finger.dest.net;
b.pup.dest.host ← finger.dest.host;
PupDefs.PupRouterSendThis[b];
RETURN;
END;
ENDLOOP;
PupDefs.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["PupDirServer"L];
IF cmFile = NIL THEN RETURN;
DO
option: Option;
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
source: PupTypes.PupNetID = [Token.Octal[cmFile]];
temp: LONG STRING ← Token.Item[cmFile, FALSE];
new: Chain ← z.NEW[ChainSlot];
new↑ ← [NIL, source, PupTypes.fillInPupAddress, z.NEW[StringBody[temp.length]]];
String.AppendString[new.target, temp];
[] ← Token.FreeTokenString[temp];
PupDefs.GetPupAddress[@new.dest, new.target ! PupDefs.PupNameTrouble => CONTINUE];
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;
MessageNet["Forwarding Pup NetDirVersion requests from net "L, new.source, " to "L, new.target];
END;
ENDCASE => ERROR;
ENDLOOP;
Indirect.Close[cmFile];
ScanParameterFileForNameServerHelpers[];
END;
-- We also have to check the NameServer Helpers...
ScanParameterFileForNameServerHelpers: PROCEDURE =
BEGIN
cmFile: CmFile.Handle;
Option: TYPE = {helper};
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 ← [helper: "Helper"L];
cmFile ← Indirect.OpenSection["NameServer"L];
IF cmFile = NIL THEN RETURN;
DO
option: Option;
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;
helper =>
BEGIN
text: STRING = [20];
temp: LONG STRING ← Token.Item[cmFile, FALSE];
new: HelperChain ← z.NEW[HelperChainSlot];
new↑ ← [NIL, PupTypes.fillInPupAddress, z.NEW[StringBody[temp.length]]];
String.AppendString[new.target, temp];
[] ← Token.FreeTokenString[temp];
PupDefs.GetPupAddress[@new.dest, new.target ! PupDefs.PupNameTrouble => CONTINUE];
IF helpers = NIL THEN helpers ← new
ELSE
BEGIN
FOR finger: HelperChain ← helpers, finger.next DO
IF finger.next = NIL THEN
BEGIN
finger.next ← new;
EXIT;
END;
ENDLOOP;
END;
END;
ENDCASE => ERROR;
ENDLOOP;
Indirect.Close[cmFile];
END;
MessageNet: PROCEDURE [one: LONG STRING, net: CARDINAL, three, four: LONG STRING] =
BEGIN
two: STRING = [20];
String.AppendNumber[two, net, 8];
Message[one, two, three, four];
END;
Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
BEGIN
text: STRING = [200];
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];
END;
-- initialization
PupDirServerOn[];
NameServerDefs.PupNameServerOn[];
NameServerDefs.BumpCacheSize[2000];
END.