DIRECTORY BasicTime USING [GMT, Now, Period], Commander USING [CommandProc, Register], Convert USING [RopeFromTime], Endian USING[BYTE, CardFromF, CardFromH, FFromCard, FWORD, HFromCard, HWORD], GenericTool USING [ButtonProc, CreateInstance, PutMsgRope, ToolHandle], IO USING [Error, PutF, PutFR, PutRope, STREAM, Value], Process USING [ConditionPointer, EnableAborts, Pause, SecondsToTicks, SetTimeout], ProcessorFace USING [GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime], Rope USING [ROPE], ViewerClasses USING [Viewer], XNS USING [Address, GetThisHost, Host, Net], XNSAddressParsing USING [MyRope], XNSRouter USING [RoutingTableEntry, Enumerate], XNSRouterPrivate USING [ClearEventProc, EventProc, SetEventProc]; XNSRouterTool: CEDAR MONITOR IMPORTS BasicTime, Commander, Convert, Endian, GenericTool, IO, Process, ProcessorFace, XNS, XNSAddressParsing, XNSRouter, XNSRouterPrivate ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Viewer: TYPE ~ ViewerClasses.Viewer; BYTE: TYPE ~ Endian.BYTE; FWORD: TYPE ~ Endian.FWORD; HWORD: TYPE ~ Endian.HWORD; ToolHandle: TYPE ~ GenericTool.ToolHandle; ButtonProc: TYPE ~ GenericTool.ButtonProc; thisHost: XNS.Host _ XNS.GetThisHost[]; toolName: ROPE ~ "XNSRouterTool"; globalToolHandle: ToolHandle; RunningType: TYPE ~ { none, showEvents, showTable }; ControlHandle: TYPE ~ REF ControlObject; ControlObject: TYPE ~ RECORD [ running: RunningType _ none, stop: ButtonProc _ Stop, showEvents: ButtonProc _ ShowEvents, showTable: ButtonProc _ ShowTable, putDelayChanges: BOOL _ TRUE, thisMachine: ROPE]; DataHandle: TYPE ~ REF DataObject; DataObject: TYPE ~ RECORD [ qHead, qTail, free: PrintEntry, qNonempty: CONDITION, pleaseStop: BOOL _ FALSE, nAdds: INT _ 0, nDelayChanges: INT _ 0, nRouteChanges: INT _ 0, nDeletes: INT _ 0 ]; Create: ENTRY PROC RETURNS [created: BOOL] ~ { cH: ControlHandle; dH: DataHandle; IF globalToolHandle # NIL THEN RETURN [FALSE]; cH _ NEW[ControlObject _ [thisMachine~XNSAddressParsing.MyRope[]] ]; dH _ NEW[DataObject _ []]; TRUSTED { InitCond[@dH.qNonempty] }; globalToolHandle _ GenericTool.CreateInstance[ toolName~toolName, control~cH, options~LIST [ ["thisMachine", readonly[]] ], data~dH, preDestroy~PreDestroy ]; RETURN [TRUE]; }; InitCond: UNSAFE PROC [cP: Process.ConditionPointer] ~ UNCHECKED { Process.EnableAborts[cP]; Process.SetTimeout[cP, Process.SecondsToTicks[1]]; }; Enter: ENTRY PROC [tH: ToolHandle, me: RunningType] RETURNS [entered: BOOL] ~ { cH: ControlHandle ~ NARROW[tH.control]; IF cH.running # none THEN { GenericTool.PutMsgRope[tH, "Tool is busy", TRUE]; RETURN [FALSE] }; cH.running _ me; RETURN [TRUE]; }; Exit: PROC [tH: ToolHandle] ~ { cH: ControlHandle ~ NARROW[tH.control]; cH.running _ none }; Stop: ButtonProc ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle ~ NARROW[tH.data]; WHILE cH.running # none DO dH.pleaseStop _ TRUE; Process.Pause[ Process.SecondsToTicks[1] ]; ENDLOOP; dH.pleaseStop _ FALSE; }; PreDestroy: ButtonProc ~ { Stop[tH]; globalToolHandle _ NIL }; PrintAborted: ERROR ~ CODE; ShowTable: ButtonProc ~ { dH: DataHandle ~ NARROW[tH.data]; count: CARDINAL _ 0; PrintProc: PROC [net: XNS.Net, rte: XNSRouter.RoutingTableEntry] ~ { IF dH.pleaseStop THEN ERROR PrintAborted[]; IO.PutRope[tH.out, (IF (count MOD 2) = 0 THEN "\n" ELSE " ")]; IO.PutF[tH.out, "%xH -> ", [cardinal[CfF[LOOPHOLE[net]]]] ]; PutRTE[tH.out, rte]; count _ count + 1; }; IF NOT Enter[tH, showTable] THEN RETURN; { ENABLE IO.Error, ABORTED => CONTINUE; aborted: BOOL _ FALSE; IO.PutF[tH.out, "\nRouting Table at %g\n", [time[BasicTime.Now[]]] ]; XNSRouter.Enumerate[proc~PrintProc ! IO.Error, ABORTED, PrintAborted => { aborted _ TRUE; CONTINUE }]; IF aborted THEN { IO.PutRope[tH.out, "\n\n ... aborted\n"]; } ELSE { IO.PutF[tH.out, "\n\n %g entries.\n", [cardinal[count]] ] }; }; Exit[tH]; }; HfC: PROC [c: CARDINAL] RETURNS [HWORD] ~ INLINE { RETURN [Endian.HFromCard[c]] }; FfC: PROC [c: LONG CARDINAL] RETURNS [FWORD] ~ INLINE { RETURN [Endian.FFromCard[c]] }; CfH: PROC [h: HWORD] RETURNS [CARDINAL] ~ INLINE { RETURN [Endian.CardFromH[h]] }; CfF: PROC [f: FWORD] RETURNS [LONG CARDINAL] ~ INLINE { RETURN [Endian.CardFromF[f]] }; RopeFromHost: PROC[h: XNS.Host] RETURNS[r: ROPE] ~ { temp: MACHINE DEPENDENT RECORD[host1: FWORD, host2: HWORD] ~ LOOPHOLE[h]; r _ IO.PutFR["%x%04xH", [cardinal[CfF[temp.host1]]], [cardinal[CfH[temp.host2]]] ]; }; PutAddress: PROC[s: STREAM, a: XNS.Address] ~ { temp: MACHINE DEPENDENT RECORD[ net: FWORD, host1: FWORD, host2: HWORD, socket: HWORD] ~ LOOPHOLE[a]; IO.PutF[s, "%xH.%x%04xH.%xH", [cardinal[CfF[temp.net]]], [cardinal[CfF[temp.host1]]], [cardinal[CfH[temp.host2]]], [cardinal[CfH[temp.socket]]] ]; }; PutRTE: PROC[s: STREAM, rte: XNSRouter.RoutingTableEntry] ~ TRUSTED { IO.PutF[s, "%xH.%g %2g %3g", [cardinal[CfF[LOOPHOLE[rte.immediate.net]]]], [rope[RopeFromHost[rte.immediate.host]]], [cardinal[rte.hops]], [cardinal[rte.time]] ]; }; PrintEntry: TYPE ~ REF PrintEntryObject; PrintEntryObject: TYPE ~ RECORD [ next: PrintEntry, kind: EntryKind, net: XNS.Net, old, new: XNSRouter.RoutingTableEntry]; EntryKind: TYPE ~ { add, change, delete }; FlushPEQueue: ENTRY PROC [dH: DataHandle] ~ { IF dH.qTail # NIL THEN { dH.qTail.next _ dH.free; dH.free _ dH.qHead; dH.qHead _ dH.qTail _ NIL }; }; PEAlloc: ENTRY PROC [dH: DataHandle] RETURNS [new: PrintEntry] ~ INLINE { IF dH.free = NIL THEN dH.free _ NEW[PrintEntryObject _ [next~dH.free, kind~, net~, old~, new~]]; new _ dH.free; dH.free _ new.next; }; PEFree: ENTRY PROC [dH: DataHandle, peH: PrintEntry] ~ INLINE { peH.next _ dH.free; dH.free _ peH }; PEEnqueue: ENTRY PROC [dH: DataHandle, peH: PrintEntry] ~ INLINE { peH.next _ NIL; IF dH.qHead = NIL THEN dH.qHead _ peH ELSE dH.qTail.next _ peH; dH.qTail _ peH; NOTIFY dH.qNonempty }; PEDequeue: ENTRY PROC [dH: DataHandle] RETURNS [peH: PrintEntry] ~ INLINE { ENABLE UNWIND => NULL; WHILE (peH _ dH.qHead) = NIL DO IF dH.pleaseStop THEN RETURN; WAIT dH.qNonempty; ENDLOOP; IF (dH.qHead _ peH.next) = NIL THEN dH.qTail _ NIL; peH.next _ NIL }; MyEventProc: XNSRouterPrivate.EventProc ~ { dH: DataHandle; peH: PrintEntry; dH _ NARROW[globalToolHandle.data]; peH _ PEAlloc[dH]; peH.net _ net; SELECT TRUE FROM (old # NIL) AND (new # NIL) => { peH.kind _ change; peH.old _ old^; peH.new _ new^ }; (new # NIL) => { peH.kind _ add; peH.new _ new^ }; (old # NIL) => { peH.kind _ delete; peH.old _ old^ }; ENDCASE => ERROR; PEEnqueue[dH, peH]; }; ShowEvents: ButtonProc ~ { dH: DataHandle ~ NARROW[tH.data]; cH: ControlHandle ~ NARROW[tH.control]; peH: PrintEntry _ NIL; startTime, stopTime: BasicTime.GMT; seconds: INT; PerMin: PROC [n: INT] RETURNS [IO.Value] ~ INLINE { RETURN [ [integer[(60*n)/seconds]] ] }; IF NOT Enter[tH, showEvents] THEN RETURN; FlushPEQueue[dH]; XNSRouterPrivate.SetEventProc[MyEventProc]; startTime _ BasicTime.Now[]; IO.PutF[tH.out, "\nStarted %g\n\n", [time[startTime]] ]; DO ENABLE IO.Error, ABORTED => EXIT; IF peH # NIL THEN PEFree[dH, peH]; peH _ PEDequeue[dH]; IF dH.pleaseStop THEN EXIT; SELECT peH.kind FROM change => { IF peH.old.immediate = peH.new.immediate THEN { dH.nDelayChanges _ dH.nDelayChanges.SUCC; IF NOT cH.putDelayChanges THEN LOOP } ELSE { dH.nRouteChanges _ dH.nRouteChanges.SUCC }; }; add => { dH.nAdds _ dH.nAdds.SUCC }; delete => { dH.nDeletes _ dH.nDeletes.SUCC }; ENDCASE => ERROR; TRUSTED { gmt: ProcessorFace.GreenwichMeanTime _ ProcessorFace.GetGreenwichMeanTime[] - ProcessorFace.gmtEpoch; IO.PutRope[tH.out, Convert.RopeFromTime[from~LOOPHOLE[gmt], start~hours, end~seconds, useAMPM~FALSE, includeZone~FALSE]]; IO.PutF[tH.out, " net %xH rte ", [cardinal[CfF[LOOPHOLE[peH.net]]]] ]; }; IF peH.kind # add THEN PutRTE[tH.out, peH.old]; IO.PutRope[tH.out, " ==> "]; IF peH.kind # delete THEN PutRTE[tH.out, peH.new]; IO.PutRope[tH.out, "\n"]; ENDLOOP; stopTime _ BasicTime.Now[]; seconds _ BasicTime.Period[from~startTime, to~stopTime]; XNSRouterPrivate.ClearEventProc[MyEventProc]; IO.PutF[tH.out, "\n\nStopped %g after %g seconds\n\n", [time[stopTime]], [integer[seconds]] ]; IF seconds <= 0 THEN seconds _ 1; IO.PutF[tH.out, "Adds: %g = %g / min\n", [integer[dH.nAdds]], PerMin[dH.nAdds] ]; IO.PutF[tH.out, "Delay Changes: %g = %g / min\n", [integer[dH.nDelayChanges]], PerMin[dH.nDelayChanges] ]; IO.PutF[tH.out, "Route Changes: %g = %g / min\n", [integer[dH.nRouteChanges]], PerMin[dH.nRouteChanges] ]; IO.PutF[tH.out, "Deletes: %g = %g / min\n", [integer[dH.nDeletes]], PerMin[dH.nDeletes] ]; IO.PutRope[tH.out, "\n\n"]; Exit[tH]; }; Go: Commander.CommandProc ~ { IF Create[] THEN RETURN [result~$Done] ELSE RETURN [result~$Failure] }; Commander.Register[key~toolName, proc~Go]; END. XNSRouterTool.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Demers, November 28, 1986 3:20:30 pm PST Tool to watch Router events. Tool Creation Print Utilities Print Daemon Initialization [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] Κ Κ˜codešœ™Kšœ Οmœ1™K˜Kšœ˜K˜—Kšžœžœžœžœ˜(š œžœžœžœžœ˜'Kšœ žœžœ˜KšžœC˜Ešœ"˜"Kš œžœžœžœžœ˜C—šžœ˜ Kšžœžœ)˜2Kšžœžœ:˜C—K˜—J˜ K˜K™——™š  œžœžœžœžœžœ˜2Kšžœ˜—š œžœžœžœžœžœžœ˜7Kšžœ˜—š  œžœžœžœžœžœ˜2Kšžœ˜—š œžœžœžœžœžœžœ˜7Kšžœ˜—K˜—š   œžœžœžœžœ˜4Kš œžœž œžœžœ žœžœ˜IKšœžœM˜SK˜K˜—š  œžœžœžœ ˜/šœžœž œžœ˜Kš œžœ žœ žœ žœžœ˜E—Kšžœ˜’K˜K˜—š œžœžœ&žœ˜Ešžœ˜Kšœžœ˜-K˜)K˜-—K˜—™ Kšœ žœžœ˜(šœžœžœ˜!Kšœ˜K˜Kšœžœ˜ Kšœ'˜'—K˜Kšœ žœ˜*K˜š  œžœžœ˜-šžœ žœžœ˜Kšœ.˜.Kšœžœ˜—K˜K˜—š  œžœžœžœžœ˜IKšžœ žœžœ žœ=˜`Kšœ˜K˜K˜K˜—š œžœžœ%žœ˜?Kšœ%˜%K˜—š  œžœžœ%žœ˜BKšœ žœ˜Kšžœ žœžœžœ˜?K˜Kšžœ˜K˜—š   œžœžœž œžœ˜KKšžœžœžœ˜šžœžœž˜Kšžœžœžœ˜Kšžœ˜Kšžœ˜—Kšžœžœžœ žœ˜3Kšœ žœ˜—K˜š  œ ˜+Kšœ˜Kšœ˜Kšœžœ˜#Kšœ˜K˜šžœžœž˜šœžœžœžœ˜ K˜6—šœžœ˜K˜"—šœžœ˜K˜%—Kšžœžœ˜—Kšœ˜K˜K˜—K˜š  œ˜Kšœžœ ˜!Kšœžœ ˜'Kšœžœ˜Kšœžœ˜#Kšœ žœ˜ š  œžœžœžœžœ žœ˜3Kšžœ!˜'—Kšžœžœžœžœ˜)K˜Kšœ+˜+Kšœ˜Kšžœ6˜8šž˜Kšžœžœžœžœ˜!Kšžœžœžœ˜"Kšœ˜Kšžœžœžœ˜šžœ ž˜˜ šžœ&˜(šžœ˜Kšœ$žœ˜)Kšžœžœžœžœ˜%—šžœ˜Kšœ$žœ˜+——K˜—˜Kšœžœ˜—˜ Kšœžœ˜!—Kšžœžœ˜—šžœh˜oKšžœ+žœ)žœžœ˜yKšžœ1žœ˜JK˜—Kšžœžœ˜/Kšžœ˜ Kšžœžœ˜2Kšžœ˜Kšžœ˜—K˜K˜8Kšœ-˜-Kšžœ\˜^Kšžœžœ ˜!KšžœO˜QKšžœh˜jKšžœh˜jKšžœX˜ZKšžœ˜J˜ K˜—K˜—™˜Kš œžœ žœžœžœžœ™:K˜šžœ ˜ Kšžœžœ˜Kšžœžœ˜—K˜K˜—Kšœ*˜*—K˜Kšžœ˜—J˜J˜J˜J˜—…—!κ.Θ