-- File: NetWatcher.mesa,  Last Edit: HGM  February 4, 1981  11:11 AM
-- Please don't forget to update the herald....

DIRECTORY
  Process USING [Detach, SetTimeout, MsecToTicks, Yield],
  String USING [AppendChar, AppendString, AppendNumber],
  Time USING [AppendCurrent],

  System USING [GreenwichMeanTime, GetGreenwichMeanTime],
  Put USING [Line],
  Tool USING [Create, MakeSWsProc, MakeMsgSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

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

NetWatcher: MONITOR
  IMPORTS
    Process, String, Time, Put, System, Tool, NameServerDefs, PupDefs,
    PupRouterDefs =
  BEGIN

  seconds: CARDINAL ← 5;

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

  maxNets: CARDINAL = PupDefs.defaultNumberOfNetworks;

  msg: Window.Handle ← NIL;
  pleaseStop, running: BOOLEAN ← FALSE;

  Init: PROCEDURE =
    BEGIN
    herald: STRING = "Net Watcher of February 4, 1981"L;
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    Process.Detach[FORK Watch[]];
    END;

  Watch: ENTRY PROCEDURE =
    BEGIN
    time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    pause: CONDITION;
    Process.SetTimeout[@pause, Process.MsecToTicks[500]];
    InitializeThings[];
    UNTIL pleaseStop DO
      WHILE (System.GetGreenwichMeanTime[] - time) < seconds DO
	WAIT pause; 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[25*wordsPerNet + 3*wordsPerPath];
    NameServerDefs.PupDirServerOn[];
    NameServerDefs.PupNameServerOn[];
    END;
    END;

  CheckNetState: INTERNAL 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];
      PupDefs.PupAddressLookup[
	text, [[net], [0], [0, 0]] !
	PupDefs.PupNameTrouble =>
	  BEGIN
	  String.AppendNumber[text, net, 8];
	  String.AppendString[text, "##"L];
	  CONTINUE;
	  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.netNumber.b], [rto.route], [0, 0]] !
	    PupDefs.PupNameTrouble =>
	      BEGIN
	      String.AppendNumber[text, rto.network.netNumber.b, 8];
	      String.AppendChar[text, '#];
	      String.AppendNumber[text, rto.route, 8];
	      String.AppendChar[text, '#];
	      CONTINUE;
	      END];
	  String.AppendString[text, via];
	  END;
      String.AppendChar[text, '.];
      IF rte = NIL OR rto.hop > PupRouterDefs.maxHop THEN
	oldPupRouting[net].network ← NIL
      ELSE oldPupRouting[net] ← rto;
      LogString[text];
      END;
      WaitTwoSeconds[];
      ENDLOOP;
    END;

  WaitTwoSeconds: INTERNAL PROCEDURE =
    BEGIN
    pause: CONDITION;
    Process.SetTimeout[@pause, Process.MsecToTicks[2000]];
    THROUGH [0..1000) DO Process.Yield[]; ENDLOOP;  -- and more if things are busy
    WAIT pause;
    END;

  LogString: PROCEDURE [text: STRING] =
    BEGIN IF msg # NIL THEN Put.Line[msg, text]; Put.Line[NIL, text]; END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN msg ← Tool.MakeMsgSW[window: window, lines: 5]; END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN IF new = inactive THEN msg ← NIL; END;


  -- initialization

  Init[];
  END.