-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- PupDirServerNoDisk.mesa, HGM,  1-Jul-84  2:01:01

-- 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 flushes the cache
-- so it will get reloaded with current information.

DIRECTORY
  Ascii USING [CR],
  Process USING [Detach, SetTimeout, MsecToTicks],
  Put USING [Text],
  String USING [AppendChar, AppendString, AppendDecimal],
  Time USING [AppendCurrent],

  Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  MiscServerDefs USING [PupMiscServerOn, SetDirectoryServer],
  NameServerDefs USING [
    lockDirRequest, lockDirReply, unlockDirRequest, unlockDirReply,
    FlushWholeCache, PupNameServerOn, BumpCacheSize],
  PupDefs USING [
    AppendHostName, GetPupContentsBytes, PupBuffer,
    PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketID,
    PupSocketMake, SecondsToTocks, SetPupContentsWords,
    ReturnPup, UniqueLocalPupSocketID],
  PupTypes USING [fillInPupAddress, miscSrvSoc, PupAddress],
  Stats USING [StatCounterIndex, StatIncr];

PupDirServerNoDisk: MONITOR
  IMPORTS
    Process, Put, String, Time,
    Buffer, MiscServerDefs, NameServerDefs, PupDefs, Stats
  EXPORTS NameServerDefs =
  BEGIN OPEN Stats, NameServerDefs, PupDefs;


  dirRunning, probing, 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;
  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[];
    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: Buffer.AccessHandle;
    soc: PupSocket;
    bestSoFar: CARDINAL;
    where: PupTypes.PupAddress;
    sawOldVersion: BOOLEAN ← FALSE;
    bestSoFar ← currentVersion;
    pool ← Buffer.MakePool[send: 1, receive: 10];
    soc ← PupSocketMake[from, PupTypes.fillInPupAddress, SecondsToTocks[1]];
    THROUGH [0..5) UNTIL bestSoFar > currentVersion 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] ← 0;
      b.pup.pupWords[1] ← 0;
      SetPupContentsWords[b, 2];
      PupRouterBroadcastThis[b];
      UNTIL b = NIL DO
        b ← soc.get[];
        IF b # NIL THEN
          BEGIN
          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;
          Buffer.ReturnBuffer[b];
          END;
        ENDLOOP;
      ENDLOOP;
    PupSocketDestroy[soc];
    Buffer.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;
          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;
    Buffer.ReturnBuffer[b];
    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.