-- 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.