-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- PupNetWatcher.mesa, HGM, 11-Feb-84 16:40:17

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Process USING [Detach, MsecToTicks, Ticks, Pause, Yield],
  Put USING [Text],
  String USING [AppendChar, AppendString, AppendNumber],
  StringLookUp USING [noMatch],
  System USING [GreenwichMeanTime, GetGreenwichMeanTime],
  Time USING [AppendCurrent],
  Token USING [Boolean],

  Indirect USING [Close, NextValue, OpenSection],
  NameServerDefs USING [BumpCacheSize, PupDirServerOn, PupNameServerOn],
  PupDefs USING [defaultNumberOfNetworks, PupAddressLookup, PupNameTrouble],
  PupRouterDefs USING [
    RoutingTableEntry, RoutingTableObject, GetRoutingTableEntry, maxHop];

PupNetWatcher: PROGRAM
  IMPORTS
    CmFile, Process, Put, String, System, Time, Token,
    Indirect, NameServerDefs, PupDefs, PupRouterDefs =
  BEGIN

  seconds: CARDINAL ← 15;
  oneSecond: Process.Ticks = Process.MsecToTicks[1000];

  oldPupRouting: ARRAY [0..maxNets] OF PupRouterDefs.RoutingTableObject;

  maxNets: CARDINAL = PupDefs.defaultNumberOfNetworks;

  Init: PROCEDURE =
    BEGIN
    off: BOOLEAN ← FALSE;
    cmFile: CmFile.Handle;
    Option: TYPE = {disable};
    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 ← [disable: "Disable"L];
    cmFile ← Indirect.OpenSection["PupNetWatcher"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      text: STRING = [200];
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError => RETRY];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        disable =>
	  BEGIN
	  off ← Token.Boolean[cmFile];
	  END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    IF ~off THEN Process.Detach[FORK Watch[]];
    END;

  Watch: PROCEDURE =
    BEGIN
    time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    Process.Pause[120*oneSecond];
    InitializeThings[];
    DO
      WHILE (System.GetGreenwichMeanTime[] - time) < seconds DO
        Process.Pause[oneSecond]; ENDLOOP;
      time ← System.GetGreenwichMeanTime[];
      CheckNetState[];
      THROUGH [0..100) DO Process.Yield[]; ENDLOOP;
      ENDLOOP;
    END;

  InitializeThings: PROCEDURE =
    BEGIN
    FOR net: CARDINAL IN (0..maxNets] DO
      rte: PupRouterDefs.RoutingTableEntry ← PupRouterDefs.GetRoutingTableEntry[
        [net]];
      IF rte = NIL OR rte.hop > PupRouterDefs.maxHop THEN
        oldPupRouting[net].network ← NIL
      ELSE oldPupRouting[net] ← rte↑;
      ENDLOOP;
    BEGIN
    wordsPerNet: CARDINAL = 20;
    wordsPerPath: CARDINAL = 40;
    NameServerDefs.BumpCacheSize[50*wordsPerNet + 3*wordsPerPath];
    NameServerDefs.PupDirServerOn[];
    NameServerDefs.PupNameServerOn[];
    END;
    END;

  CheckNetState: PROCEDURE =
    BEGIN
    FOR net: CARDINAL IN (0..maxNets] DO
      rte: PupRouterDefs.RoutingTableEntry ← PupRouterDefs.GetRoutingTableEntry[
        [net]];
      rto: PupRouterDefs.RoutingTableObject;
      -- It would be nice if there were a simple way to do this atomically
      IF rte # NIL THEN rto ← rte↑;
      IF (rte = NIL AND oldPupRouting[net].network = NIL)
        OR (rto.network = NIL AND oldPupRouting[net].network = NIL)
        OR (rto.hop > PupRouterDefs.maxHop AND oldPupRouting[net].network = NIL)
        OR
          (oldPupRouting[net].network = rto.network
            AND oldPupRouting[net].route = rto.route
            AND oldPupRouting[net].hop = rto.hop) THEN LOOP;
      BEGIN
      text: STRING = [100];
      via: STRING = [20];
      Time.AppendCurrent[text];
      String.AppendString[text, "  "L];
      BEGIN ENABLE PupDefs.PupNameTrouble =>
          BEGIN
          String.AppendNumber[text, net, 8];
          String.AppendString[text, "##"L];
          CONTINUE;
          END;
      PupDefs.PupAddressLookup[text, [[net], [0], [0, 0]]];
      String.AppendString[text, " ("L];
      String.AppendNumber[text, net, 8];
      String.AppendString[text, "##)"L];
      END;
      String.AppendString[text, " is "L];
      SELECT TRUE FROM
        rte = NIL, rto.network = NIL, rto.hop > PupRouterDefs.maxHop =>
          String.AppendString[text, "unreachable"L];
        (rto.route = 0) => String.AppendString[text, "directly connected"L];
        ENDCASE =>
          BEGIN
          String.AppendNumber[text, rto.hop, 10];
          String.AppendString[text, " hop"L];
          IF rto.hop # 1 THEN String.AppendChar[text, 's];
          String.AppendString[text, " via "L];
          PupDefs.PupAddressLookup[
            via, [[rto.network.pupNetNumber], [rto.route], [0, 0]] !
            PupDefs.PupNameTrouble =>
              BEGIN
              String.AppendNumber[text, rto.network.pupNetNumber, 8];
              String.AppendChar[text, '#];
              String.AppendNumber[text, rto.route, 8];
              String.AppendChar[text, '#];
              CONTINUE;
              END];
          String.AppendString[text, via];
          END;
      LogString[text];
      IF rte = NIL OR rto.hop > PupRouterDefs.maxHop THEN
        oldPupRouting[net].network ← NIL
      ELSE oldPupRouting[net] ← rto;
      END;
      THROUGH [0..1000) DO Process.Yield[]; ENDLOOP;  -- Wait longer if things are busy
      Process.Pause[5*oneSecond];
      ENDLOOP;
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    END;


  -- initialization

  Init[];
  END.