XNSSpyTool.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Demers, December 9, 1986 10:12:19 am PST
Hal Murray, May 5, 1986 11:42:59 pm PDT
Tool to watch all XNS packets received.
DIRECTORY
BasicTime USING [earliestGMT, GMT, Now],
Commander USING [CommandProc, Register],
CommBuffer USING [Overhead],
CommDriver USING [Buffer, BufferObject, CreateInterceptor, DestroyInterceptor, GetNetworkChain, Interceptor, Network, RecvInterceptor, SendInterceptor],
Convert USING [RopeFromCard],
Endian USING[BYTE, CardFromF, CardFromH, FFromCard, FWORD, HFromCard, HWORD],
GenericTool USING [ButtonProc, CreateInstance, PutMsgRope, ToolHandle],
IO USING [Error, PutChar, PutF, PutFR, PutRope, STREAM],
PrincOpsUtils USING [LongMove],
Process USING [ConditionPointer, EnableAborts, Pause, SecondsToTicks, SetTimeout],
Pup USING [Host],
Rope USING [Concat, FromChar, Length, ROPE],
ViewerClasses USING [Viewer],
XNS USING [Address, broadcastHost, GetThisHost, Host, Net, unknownHost],
XNSAddressParsing USING [AddressFromRope, Error, Format, MyRope, RopeFromAddress],
XNSBuf USING [Buffer, hdrBytes],
XNSEchoBuf USING [Buffer],
XNSErrorBuf USING [Buffer, minBodyBytes],
XNSErrorTypes USING [badChecksumErr, ErrorType, invalidPacketTypeErr, listenerRejectErr, noSocketErr, protocolViolationErr, resourceLimitsErr],
XNSExchangeBuf USING [Buffer, hdrBytes],
XNSExchangeTypes USING [ExchangeType, clearinghouseServiceType, teledebugType, timeServiceType],
XNSRoutingBuf USING [Buffer, NumTuplesFromUserBytes],
XNSSocket USING [GetUserBytes],
XNSSPPBuf USING [Buffer, hdrBytes],
XNSSPPTypes USING [];
XNSSpyTool: CEDAR MONITOR
IMPORTS BasicTime, Commander, CommDriver, Convert, Endian, GenericTool, IO, PrincOpsUtils, Process, Rope, XNS, XNSAddressParsing, XNSRoutingBuf, XNSSocket
~ 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 ~ "XNSSpyTool";
globalToolHandle: ToolHandle;
RunningType: TYPE ~ { none, watch, stats };
ControlHandle: TYPE ~ REF ControlObject;
ControlObject: TYPE ~ RECORD [
running: RunningType ← none,
stop: ButtonProc ← Stop,
watch: ButtonProc ← Watch,
stats: ButtonProc ← Stats,
putRouting: BOOLFALSE,
putEcho: BOOLFALSE,
putError: BOOLFALSE,
putExchange: BOOLFALSE,
putSPP: BOOLFALSE,
putTranslation: BOOLFALSE,
shortFormat: BOOLFALSE,
maxLength: CARDINAL ← 576,
thisMachine: ROPE,
to: ROPE,
from: ROPE];
DataHandle: TYPE ~ REF DataObject;
DataObject: TYPE ~ RECORD [
interceptor: CommDriver.Interceptor ← NIL,
printQHead, printQTail, printQFree: PrintBuf,
printQNonempty: CONDITION,
pleaseStop: BOOLFALSE,
promiscuous: BOOLFALSE,
fromHost: XNS.Host ← XNS.unknownHost,
toHost: XNS.Host ← XNS.unknownHost,
nTooLong: INT ← 0,
nEcho: INT ← 0,
nError: INT ← 0,
nExchange: INT ← 0,
nRouting: INT ← 0,
nSPP: INT ← 0,
nTranslation: 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.printQNonempty] };
globalToolHandle ← GenericTool.CreateInstance[
toolName~toolName,
control~cH,
options~LIST[
["thisMachine", readonly[]],
["to", notify[GetFilterAddresses]],
["from", notify[GetFilterAddresses]]
],
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 };
GetFilterAddresses: ButtonProc ~ {
dH: DataHandle ~ NARROW[tH.data];
cH: ControlHandle ~ NARROW[tH.control];
gotTo, gotFrom: BOOL;
{
ENABLE XNSAddressParsing.Error => {
GenericTool.PutMsgRope[tH, text, TRUE]; CONTINUE };
gotTo ← gotFrom ← FALSE;
dH.toHost ← dH.fromHost ← thisHost;
IF Rope.Length[cH.to] > 0 THEN {
dH.toHost ← XNSAddressParsing.AddressFromRope[cH.to].host;
gotTo ← TRUE };
IF Rope.Length[cH.from] > 0 THEN {
dH.fromHost ← XNSAddressParsing.AddressFromRope[cH.from].host;
gotFrom ← TRUE };
};
dH.promiscuous ← (gotTo OR gotFrom);
};
Stats: ButtonProc ~ {
dH: DataHandle ~ NARROW[tH.data];
cH: ControlHandle ~ NARROW[tH.control];
IF NOT Enter[tH, stats] THEN RETURN;
{ ENABLE IO.Error, ABORTED => CONTINUE;
IO.PutF[tH.out, "\n%7g echo\n%7g error\n%7g exchange\n%7g routing\n%7g spp\n", [integer[dH.nEcho]], [integer[dH.nError]], [integer[dH.nExchange]], [integer[dH.nRouting]], [integer[dH.nSPP]] ];
IO.PutF[tH.out, "\n%7g translation\n", [integer[dH.nTranslation]] ];
IO.PutF[tH.out, "\n%7g dropped (too long)\n", [integer[dH.nTooLong]] ];
IO.PutF[tH.out, "\n%7g total\n\n", [integer[
dH.nEcho+dH.nError+dH.nExchange+dH.nRouting+dH.nSPP+dH.nTranslation]] ];
};
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]]] ];
};
XNS Buffers
DisplayXNSBuf: PROC [s: STREAM, b: XNSBuf.Buffer, heading: ROPE, when: BasicTime.GMT, short: BOOL] ~ {
Print an XNS buffer on s. No assumptions about encapsulation.
PutHdr: PROC [typeName: ROPE] ~ {
IO.PutF[s, "XNS %g %g:\nlen %g, dst ", [rope[heading]], [time[when]], [cardinal[CfH[b.hdr1.length]]] ];
PutAddress[s, b.hdr1.dest];
IO.PutRope[s, ", src "];
PutAddress[s, b.hdr1.source];
IO.PutF[s, ", type %g\n", [rope[typeName]] ];
};
SELECT b.hdr1.type FROM
routing => {
PutHdr["routing"];
TRUSTED { PutRoutingBuf[s, LOOPHOLE[b], short] }};
echo => {
PutHdr["echo"];
TRUSTED { PutEchoBuf[s, LOOPHOLE[b], short] }};
error => {
PutHdr["error"];
TRUSTED { PutErrorBuf[s, LOOPHOLE[b], short] }};
exchange => {
PutHdr["exchange"];
TRUSTED { PutExchangeBuf[s, LOOPHOLE[b], short] }};
spp => {
PutHdr["spp"];
TRUSTED { PutSPPBuf[s, LOOPHOLE[b], short] }};
ENDCASE => {
PutHdr[Convert.RopeFromCard[ORD[b.hdr1.type], 16]] };
};
PutRoutingBuf: PROC [s: STREAM, b: XNSRoutingBuf.Buffer, short: BOOL] ~ {
Print contents of a routing information packet.
numTuples: NAT ~ XNSRoutingBuf.NumTuplesFromUserBytes[b.hdr1.length - XNSBuf.hdrBytes];
numToPrint: NAT;
IO.PutF[s, "routingType %g, %g tuples:\n",
[rope[ SELECT b.hdr2.type FROM
request => "request", response => "response", ENDCASE => "???"]],
[cardinal[numTuples]]
];
numToPrint ← numTuples;
IF short THEN numToPrint ← MIN[numToPrint, 5];
FOR i: CARDINAL IN [0 .. numToPrint) DO
IO.PutF[s, "[%09x,%2g] ", [cardinal[CfF[LOOPHOLE[b.body.tuples[i].net]]]], [cardinal[CfH[b.body.tuples[i].delay]]] ];
ENDLOOP;
IF numToPrint < numTuples THEN IO.PutRope[s, " ..."];
IO.PutChar[s, '\n];
};
PutEchoBuf: PROC [s: STREAM, b: XNSEchoBuf.Buffer, short: BOOL] ~ {
Put generic part of echo packet.
IO.PutF[s, "echoType %g\n",
[rope[SELECT b.hdr2.type FROM
request => "request",
reply => "reply",
ENDCASE => Convert.RopeFromCard[ORD[b.hdr2.type]]]]
];
};
RopeFromErrorType: PROC [t: XNSErrorTypes.ErrorType] RETURNS [r: ROPE] ~ INLINE {
SELECT TRUE FROM
t = XNSErrorTypes.badChecksumErr => r ← "badChecksum";
t = XNSErrorTypes.noSocketErr => r ← "noSocket";
t = XNSErrorTypes.resourceLimitsErr => r ← "resourceLimit";
t = XNSErrorTypes.listenerRejectErr => r ← "listenerReject";
t = XNSErrorTypes.invalidPacketTypeErr => r ← "invalidPacketType";
t = XNSErrorTypes.protocolViolationErr => r ← "protocolViolation";
ENDCASE => r ← Convert.RopeFromCard[t.a*256+t.b, 16];
};
PutErrorBuf: PROC [s: STREAM, b: XNSErrorBuf.Buffer, short: BOOL] ~ {
Print generic part of an error packet.
Note this clobbers the content of the buffer!
IO.PutF[s, "errorType %g, param %g\n",
[rope[ RopeFromErrorType[b.hdr2.type] ]],
[cardinal[CfH[b.hdr2.param]]]
];
IF NOT short THEN {
nsb: XNSBuf.Buffer;
TRUSTED { nsb ← LOOPHOLE[b] };
IF XNSSocket.GetUserBytes[nsb] >= XNSErrorBuf.minBodyBytes
THEN TRUSTED {
PrincOpsUtils.LongMove[
from: @b.body,
nwords: (XNSErrorBuf.minBodyBytes+1)/2,
to: @nsb.hdr1];
DisplayXNSBuf[s, nsb, "error body", BasicTime.earliestGMT, TRUE];
}
ELSE {
IO.PutF[s, "(short body)\n"];
};
};
};
RopeFromExchangeType: PROC [t: XNSExchangeTypes.ExchangeType]
RETURNS [r: ROPE] ~ INLINE {
SELECT TRUE FROM
t = XNSExchangeTypes.timeServiceType => r ← "timeService";
t = XNSExchangeTypes.clearinghouseServiceType => r ← "clearinghouseService";
t = XNSExchangeTypes.teledebugType => r ← "teledebug";
ENDCASE => r ← Convert.RopeFromCard[t.a*256+t.b, 16];
};
PutExchangeBuf: PROC [s: STREAM, b: XNSExchangeBuf.Buffer, short: BOOL] ~ {
Print generic part of an exchange packet.
IO.PutF[s, "exchangeID %g, exchangeType %g\n",
[cardinal[ CfF[b.hdr2.id] ]],
[rope[ RopeFromExchangeType[b.hdr2.type] ]]
];
{
nBytes: INT ← CfH[b.hdr1.length];
nHWords, nToPrint: INT;
nBytes ← nBytes - XNSBuf.hdrBytes - XNSExchangeBuf.hdrBytes;
IO.PutF[s, "len %g, data", [integer[nBytes]]];
nToPrint ← nHWords ← (nBytes + 1) / 2;
IF short THEN nToPrint ← MIN[nToPrint, 8];
FOR i: INT IN [0 .. nToPrint) DO
IO.PutF[s, " %g", [cardinal[CfH[b.body.hWords[i]]]] ];
ENDLOOP;
IF nToPrint < nHWords THEN IO.PutF[s, " ..."];
IO.PutChar[s, '\n];
};
};
PutSPPBuf: PROC [s: STREAM, b: XNSSPPBuf.Buffer, short: BOOL] ~ {
Print generic part of an SPP packet.
IO.PutF[s, "[ "];
IF b.hdr2.connCtl.system THEN IO.PutF[s, "system "];
IF b.hdr2.connCtl.sendAck THEN IO.PutF[s, "sendAck "];
IF b.hdr2.connCtl.attn THEN IO.PutF[s, "attention "];
IF b.hdr2.connCtl.endOfMsg THEN IO.PutF[s, "endOfMsg "];
IO.PutF[s, "], "];
IO.PutF[s, "sst %g, srcID %g, destID %g, ",
[cardinal[b.hdr2.sst]], [cardinal[CfH[b.hdr2.sourceConnID]]], [cardinal[CfH[b.hdr2.destConnID]]] ];
IO.PutF[s, "seq %g, ack %g, alloc %g\n",
[cardinal[CfH[b.hdr2.seqNum]]], [cardinal[CfH[b.hdr2.ackNum]]], [cardinal[CfH[b.hdr2.allocNum]]] ];
{
nBytes: INT ← CfH[b.hdr1.length];
nHWords, nToPrint: INT;
nBytes ← nBytes - XNSBuf.hdrBytes - XNSSPPBuf.hdrBytes;
IO.PutF[s, "len %g, data", [integer[nBytes]]];
nToPrint ← nHWords ← (nBytes + 1) / 2;
IF short THEN nToPrint ← MIN[nToPrint, 8];
FOR i: INT IN [0 .. nToPrint) DO
IO.PutF[s, " %g", [cardinal[CfH[b.body.hWords[i]]]] ];
ENDLOOP;
IF nToPrint < nHWords THEN IO.PutF[s, " ..."];
IO.PutChar[s, '\n];
};
};
Translation Buffers
TranslationType: TYPE ~ MACHINE DEPENDENT RECORD [a, b: BYTE];
requestType: TranslationType ~ [010H, 041H];
replyType: TranslationType ~ [00eH, 038H];
HostPair: TYPE ~ MACHINE DEPENDENT RECORD [
xnsHost: XNS.Host,
pupHost: Pup.Host,
filler: BYTE];
TranslationBuf: TYPE ~ REF TranslationBufObject;
TranslationBufObject: TYPE ~ MACHINE DEPENDENT RECORD [
ovh: CommBuffer.Overhead,
translationType: TranslationType,
replier: HostPair,
requestor: HostPair];
putTranslation: BOOLFALSE;
PutHostPair: PROC [s: STREAM, hosts: HostPair] ~ INLINE {
IO.PutF[s, "%g -> %g", [rope[RopeFromHost[hosts.xnsHost]]], [cardinal[hosts.pupHost]]];
};
DisplayTranslationBuf: PROC [s: STREAM, b: TranslationBuf, heading: ROPE, when: BasicTime.GMT, short: BOOL] ~ {
Print an XNSEthernetOneTranslation buffer on s. No assumptions about encapsulation.
t: TranslationType ~ b.translationType;
IO.PutF[s, "Translation %g %g:\ntype %g, ", [rope[heading]], [time[when]],
[rope[SELECT TRUE FROM
t = requestType => "request",
t = replyType => "reply",
ENDCASE => "???"]]
];
IO.PutRope[s, "replier "]; PutHostPair[s, b.replier]; IO.PutRope[s, ", "];
IO.PutRope[s, "requestor "]; PutHostPair[s, b.requestor];
IO.PutChar[s, '\n];
};
Send and Receive Interceptor Procs
Recv: CommDriver.RecvInterceptor
[recv: RecvType, data: REF ANY, network: Network, buffer: Buffer, bytes: NAT] RETURNS [kill: BOOLFALSE]
~ {
tH: ToolHandle;
dH: DataHandle;
cH: ControlHandle;
tH ← NARROW[data];
dH ← NARROW[tH.data];
cH ← NARROW[tH.control];
IF bytes > cH.maxLength THEN {
dH.nTooLong ← dH.nTooLong.SUCC;
RETURN };
SELECT recv FROM
xns => {
b: XNSBuf.Buffer;
TRUSTED { b ← LOOPHOLE[buffer] };
IF dH.promiscuous
THEN {
SELECT TRUE FROM
(b.hdr1.source.host = dH.fromHost) => {
PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~send] };
(b.hdr1.dest.host = dH.toHost) => {
PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv] };
(b.hdr1.dest.host = XNS.broadcastHost) => {
PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv] };
ENDCASE;
}
ELSE {
PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv];
};
};
xnsTranslate => {
PostPrintRequest[tH~tH, kind~translation, buffer~buffer, direction~recv];
};
ENDCASE => NULL;
};
Send: CommDriver.SendInterceptor
[send: SendType, data: REF ANY, network: Network, buffer: Buffer, bytes: NAT] RETURNS [kill: BOOLFALSE]]
~ {
tH: ToolHandle;
dH: DataHandle;
cH: ControlHandle;
tH ← NARROW[data];
dH ← NARROW[tH.data];
cH ← NARROW[tH.control];
IF dH.promiscuous THEN RETURN;
IF bytes > cH.maxLength THEN {
dH.nTooLong ← dH.nTooLong.SUCC;
RETURN };
SELECT send FROM
xns, xnsReturn => {
b: XNSBuf.Buffer;
TRUSTED { b ← LOOPHOLE[buffer] };
PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~send];
};
xnsTranslate => {
PostPrintRequest[tH~tH, kind~translation, buffer~buffer, direction~send];
};
ENDCASE => NULL;
};
Install: ENTRY PROC [tH: ToolHandle] ~ {
dH: DataHandle ~ NARROW[tH.data];
network: CommDriver.Network ~ CommDriver.GetNetworkChain[];
IF dH.interceptor # NIL THEN RETURN;
IF network = NIL THEN RETURN;
dH.interceptor ← CommDriver.CreateInterceptor[
network~network,
sendMask~[arpa~FALSE, arpaReturn~FALSE, arpaTranslate~FALSE, xns~TRUE, xnsReturn~TRUE, xnsTranslate~TRUE, pup~FALSE, pupReturn~FALSE, pupTranslate~FALSE, other~FALSE, otherReturn~FALSE, otherTranslate~FALSE, raw~FALSE],
sendProc~Send,
recvMask~[arpa~FALSE, arpaTranslate~FALSE, xns~TRUE, xnsTranslate~TRUE, pup~FALSE, pupTranslate~FALSE, other~FALSE, otherTranslate~FALSE, error~FALSE],
recvProc~Recv,
data~tH,
promiscuous~dH.promiscuous];
};
UnInstall: ENTRY PROC [tH: ToolHandle] ~ {
dH: DataHandle ~ NARROW[tH.data];
IF dH.interceptor = NIL THEN RETURN;
CommDriver.DestroyInterceptor[dH.interceptor];
dH.interceptor ← NIL };
Print Daemon
The Daemon actually prints buffers that have been queued by Recv and Translate procs.
BufKind: TYPE ~ { ns, translation };
BufDirection: TYPE ~ { send, recv };
PrintBuf: TYPE ~ REF PrintBufObject;
PrintBufObject: TYPE ~ RECORD [
bufObject: CommDriver.BufferObject,
time: BasicTime.GMT,
kind: BufKind,
direction: BufDirection,
next: PrintBuf];
FlushPrintQueue: ENTRY PROC [dH: DataHandle] ~ {
IF dH.printQTail # NIL THEN {
dH.printQTail.next ← dH.printQFree; dH.printQFree ← dH.printQHead;
dH.printQHead ← dH.printQTail ← NIL };
};
EnqueuePrintBuf: ENTRY PROC [dH: DataHandle, b: PrintBuf] ~ INLINE {
IF dH.printQHead = NIL
THEN {
dH.printQHead ← dH.printQTail ← b;
NOTIFY dH.printQNonempty }
ELSE {
dH.printQTail.next ← b;
dH.printQTail ← b };
b.next ← NIL
};
DequeuePrintBuf: ENTRY PROC [dH: DataHandle] RETURNS [b: PrintBuf] ~ INLINE {
ENABLE UNWIND => NULL;
WHILE (b ← dH.printQHead) = NIL DO
IF dH.pleaseStop THEN RETURN;
WAIT dH.printQNonempty
ENDLOOP;
dH.printQHead ← b.next;
b.next ← NIL;
};
AllocPrintBuf: ENTRY PROC [dH: DataHandle] RETURNS [b: PrintBuf] ~ INLINE {
IF (b ← dH.printQFree) # NIL
THEN {
dH.printQFree ← b.next;
b.next ← NIL }
ELSE {
b ← NEW[PrintBufObject ← [bufObject~, time~, kind~, direction~, next~NIL]] };
};
FreePrintBuf: ENTRY PROC [dH: DataHandle, b: PrintBuf] ~ INLINE {
b.next ← dH.printQFree; dH.printQFree ← b;
};
PostPrintRequest: PROC [tH: ToolHandle, kind: BufKind, buffer: CommDriver.Buffer, direction: BufDirection] ~ {
cH: ControlHandle ~ NARROW[tH.control];
dH: DataHandle ← NARROW[tH.data];
b: PrintBuf;
SELECT kind FROM
ns => {
nsb: XNSBuf.Buffer;
TRUSTED { nsb ← LOOPHOLE[buffer] };
SELECT nsb.hdr1.type FROM
routing => {
dH.nRouting ← dH.nRouting + 1;
IF NOT cH.putRouting THEN RETURN };
echo => {
dH.nEcho ← dH.nEcho + 1;
IF NOT cH.putEcho THEN RETURN };
error => {
dH.nError ← dH.nError + 1;
IF NOT cH.putError THEN RETURN };
exchange => {
dH.nExchange ← dH.nExchange + 1;
IF NOT cH.putExchange THEN RETURN };
spp => {
dH.nSPP ← dH.nSPP + 1;
IF NOT cH.putSPP THEN RETURN };
ENDCASE => {
NULL };
};
translation => {
IF NOT cH.putTranslation THEN RETURN };
ENDCASE => ERROR;
b ← AllocPrintBuf[dH];
b.bufObject ← buffer^;
b.bufObject.ovh.next ← NIL;
b.bufObject.ovh.network ← NIL;
b.time ← BasicTime.Now[];
b.kind ← kind;
b.direction ← direction;
EnqueuePrintBuf[dH, b];
};
Watch: PROC [tH: ToolHandle] ~ {
cH: ControlHandle ~ NARROW[tH.control];
dH: DataHandle ~ NARROW[tH.data];
b: PrintBuf;
IF NOT Enter[tH, watch] THEN RETURN;
FlushPrintQueue[dH];
Install[tH];
IO.PutF[tH.out, "\nStarted %g\n\n", [time[BasicTime.Now[]]] ];
DO
ENABLE IO.Error, ABORTED => EXIT;
b ← DequeuePrintBuf[dH];
IF dH.pleaseStop THEN EXIT;
IF b = NIL THEN LOOP;
SELECT b.direction FROM
send => SELECT b.kind FROM
ns => TRUSTED {
DisplayXNSBuf[tH.out, LOOPHOLE[b], "Send", b.time, cH.shortFormat] };
translation => TRUSTED {
DisplayTranslationBuf[tH.out, LOOPHOLE[b], "Send", b.time, cH.shortFormat] };
ENDCASE => ERROR;
recv => SELECT b.kind FROM
ns => TRUSTED {
DisplayXNSBuf[tH.out, LOOPHOLE[b], "Recv", b.time, cH.shortFormat] };
translation => TRUSTED {
DisplayTranslationBuf[tH.out, LOOPHOLE[b], "Recv", b.time, cH.shortFormat] };
ENDCASE => ERROR;
ENDCASE;
IO.PutChar[tH.out, '\n];
FreePrintBuf[dH, b];
ENDLOOP;
IO.PutF[tH.out, "\n\nStopped %g\n", [time[BasicTime.Now[]]] ];
UnInstall[tH];
Exit[tH];
};
Conversion Procs (for use from interpreter)
CvtRopeFromCardinals: PROC [args: LIST OF CARDINAL] RETURNS [result: ROPENIL] ~ {
WHILE args # NIL DO
n: CARDINAL ~ args.first;
result ← Rope.Concat[result, Rope.FromChar[VAL[n/256]]];
result ← Rope.Concat[result, Rope.FromChar[VAL[n MOD 256]]];
args ← args.rest;
ENDLOOP;
};
CvtAddrFromCardinals: PROC [args: LIST OF CARDINAL,
format: XNSAddressParsing.Format ← productSoftware]
RETURNS [result: ROPENIL] ~ {
buf: ARRAY [0..6) OF CARDINAL;
FOR i: CARDINAL IN [0..6) DO
IF args = NIL THEN RETURN ["args too short"];
buf[i] ← args.first; args ← args.rest;
ENDLOOP;
TRUSTED {
result ← XNSAddressParsing.RopeFromAddress[LOOPHOLE[buf], format] };
};
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.