XNSRouterTool.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Demers, November 28, 1986 3:20:30 pm PST
Tool to watch Router events.
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[];
Tool Creation
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: BOOLTRUE,
thisMachine: ROPE];
DataHandle: TYPE ~ REF DataObject;
DataObject: TYPE ~ RECORD [
qHead, qTail, free: PrintEntry,
qNonempty: CONDITION,
pleaseStop: BOOLFALSE,
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: BOOLFALSE;
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];
};
Print Utilities
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]] ];
};
Print Daemon
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];
};
Initialization
Go: Commander.CommandProc
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
~ {
IF Create[]
THEN RETURN [result~$Done]
ELSE RETURN [result~$Failure]
};
Commander.Register[key~toolName, proc~Go];
END.