-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- NetWatcher.mesa, HGM, 11-Feb-84 16:41:36 DIRECTORY Ascii USING [CR], CmFile USING [Handle, TableError], Format USING [HostNumber, NetworkNumber], Heap USING [systemZone], Process USING [Detach, MsecToTicks, Ticks, Pause, Yield], Put USING [Text], Runtime USING [UnboundProcedure], String USING [AppendChar, AppendString, AppendNumber], StringLookUp USING [noMatch], System USING [ GreenwichMeanTime, GetGreenwichMeanTime, HostNumber, NetworkNumber, nullHostNumber, nullNetworkNumber], Time USING [AppendCurrent], Token USING [Boolean], Indirect USING [Close, NextValue, OpenSection], InrFriends USING [DriverDetails, GetRouteInfo], Router USING [ endEnumeration, EnumerateRoutingTable, GetDelayToNet, infinity, NoTableEntryForNet, startEnumeration]; NetWatcher: PROGRAM IMPORTS CmFile, Format, Heap, Process, Put, Runtime, String, System, Time, Token, Indirect, InrFriends, Router = BEGIN z: UNCOUNTED ZONE = Heap.systemZone; seconds: CARDINAL ← 60; oneSecond: Process.Ticks = Process.MsecToTicks[1000]; first: Handle ← NIL; Handle: TYPE = LONG POINTER TO Object; Object: TYPE = RECORD [ next: Handle, net: System.NetworkNumber, viaNet: System.NetworkNumber, viaHost: System.HostNumber, old: BOOLEAN, delay: CARDINAL ]; 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["NetWatcher"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]; EnumerateRoutingTable[AddEntry]; DO WHILE (System.GetGreenwichMeanTime[] - time) < seconds DO Process.Pause[oneSecond]; ENDLOOP; time ← System.GetGreenwichMeanTime[]; MarkOldNets[]; EnumerateRoutingTable[AddAndPrint]; CheckOldNets[]; THROUGH [0..100) DO Process.Yield[]; ENDLOOP; ENDLOOP; END; EnumerateRoutingTable: PROCEDURE [ proc: PROCEDURE [ net: System.NetworkNumber, delay: CARDINAL, viaNet: System.NetworkNumber, viaHost: System.HostNumber] ] = BEGIN net: System.NetworkNumber ← Router.startEnumeration; FOR delay: CARDINAL IN [0..Router.infinity) DO net: System.NetworkNumber ← Router.startEnumeration; DO details: InrFriends.DriverDetails; net ← Router.EnumerateRoutingTable[net, delay]; IF net = Router.endEnumeration THEN EXIT; [, details] ← InrFriends.GetRouteInfo[net ! Runtime.UnboundProcedure, Router.NoTableEntryForNet => BEGIN details.driverNetwork ← System.nullNetworkNumber; details.via.host ← System.nullHostNumber; CONTINUE; END]; proc[net, delay, details.driverNetwork, details.via.host]; ENDLOOP; ENDLOOP; END; AddEntry: PROCEDURE [ net: System.NetworkNumber, delay: CARDINAL, viaNet: System.NetworkNumber, viaHost: System.HostNumber] = BEGIN handle: Handle; handle ← z.NEW[Object]; handle↑ ← [ next: first, net: net, viaNet: viaNet, viaHost: viaHost, old: FALSE, delay: delay ]; first ← handle; END; AddAndPrint: PROCEDURE [ net: System.NetworkNumber, delay: CARDINAL, viaNet: System.NetworkNumber, viaHost: System.HostNumber] = BEGIN FOR handle: Handle ← first, handle.next UNTIL handle = NIL DO IF handle.net = net THEN BEGIN handle.old ← FALSE; RETURN; END; ENDLOOP; AddEntry[net, delay, viaNet, viaHost]; IF first.delay = Router.infinity THEN RETURN; -- Routing fault: still unreachable PrintMessage[first]; END; MarkOldNets: PROCEDURE = BEGIN FOR handle: Handle ← first, handle.next UNTIL handle = NIL DO handle.old ← TRUE; ENDLOOP; END; CheckOldNets: PROCEDURE = BEGIN handle: Handle ← first; UNTIL handle = NIL DO delay: CARDINAL ← Router.infinity; details: InrFriends.DriverDetails; IF handle.old THEN BEGIN temp: Handle ← handle; IF handle.delay # Router.infinity THEN BEGIN handle.delay ← Router.infinity; PrintMessage[handle]; END; IF first = handle THEN BEGIN first ← handle.next; END ELSE BEGIN FOR prev: Handle ← first, prev.next DO IF prev.next = handle THEN BEGIN prev.next ← handle.next; EXIT; END; ENDLOOP; END; handle ← handle.next; z.FREE[@temp]; LOOP; END; delay ← Router.GetDelayToNet[handle.net ! Router.NoTableEntryForNet => CONTINUE]; [, details] ← InrFriends.GetRouteInfo[handle.net ! Runtime.UnboundProcedure, Router.NoTableEntryForNet => BEGIN details.driverNetwork ← System.nullNetworkNumber; details.via.host ← System.nullHostNumber; CONTINUE; END]; IF handle.delay # delay OR handle.viaNet # details.driverNetwork OR handle.viaHost # details.via.host THEN BEGIN handle.delay ← delay; handle.viaNet ← details.driverNetwork; handle.viaHost ← details.via.host; PrintMessage[handle]; END; handle ← handle.next; ENDLOOP; END; PrintMessage: PROCEDURE [handle: Handle] = BEGIN text: STRING = [200]; net: RECORD [a, b: WORD] = LOOPHOLE[handle.net]; Time.AppendCurrent[text]; String.AppendString[text, " Net "L]; AppendNetNumber[text, handle.net]; IF net.b < 256 THEN BEGIN String.AppendString[text, " ("L]; AppendNetNumberOctal[text, handle.net]; String.AppendString[text, ")"L]; END; String.AppendString[text, " is "L]; SELECT TRUE FROM handle.delay = Router.infinity => String.AppendString[text, "unreachable"L]; (handle.delay = 0) => String.AppendString[text, "directly connected"L]; ENDCASE => BEGIN String.AppendNumber[text, handle.delay, 10]; String.AppendString[text, " hops via "L]; AppendNetNumber[text, handle.viaNet]; String.AppendChar[text, '.]; AppendHostNumber[text, handle.viaHost]; String.AppendChar[text, '.]; END; LogString[text]; THROUGH [0..1000) DO Process.Yield[]; ENDLOOP; -- Wait longer if things are busy Process.Pause[5*oneSecond]; END; AppendNetNumber: PROCEDURE [string: LONG STRING, net: System.NetworkNumber] = BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[string, s]; END; Format.NetworkNumber[Append, net, productSoftware]; END; AppendNetNumberOctal: PROCEDURE [string: LONG STRING, net: System.NetworkNumber] = BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[string, s]; END; Format.NetworkNumber[Append, net, octal]; END; AppendHostNumber: PROCEDURE [string: LONG STRING, host: System.HostNumber] = BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[string, s]; END; Format.HostNumber[Append, host, productSoftware]; END; AppendNetAndHost: PROCEDURE [ string: LONG STRING, net: System.NetworkNumber, host: System.HostNumber] = BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[string, s]; END; Format.NetworkNumber[Append, net, productSoftware]; String.AppendChar[string, '.]; Format.HostNumber[Append, host, productSoftware]; String.AppendChar[string, '.]; END; LogString: PROCEDURE [text: LONG STRING] = BEGIN String.AppendChar[text, '.]; String.AppendChar[text, Ascii.CR]; Put.Text[NIL, text]; END; Init[]; END.