<> <> <> <<>> <> <<>> 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 <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> ~ { IF Create[] THEN RETURN [result~$Done] ELSE RETURN [result~$Failure] }; Commander.Register[key~toolName, proc~Go]; END.