-- Pupwatch: communications and user interface
-- [Indigo]<Grapevine>PupWatch>Looker.mesa
-- Andrew Birrell 23-Nov-81 11:24:44 --
DIRECTORY
-- Look on [ivy]<Juniper>n.n>User> for Pine bcds (currently n.n = 6.1).
Ascii USING[ BS, ControlW, CR, DEL, ESC, SP ],
--CommonPineDefs USING[DataRequest, Parameters],
DisplayDefs USING[ DestroyDisplay ],
FrameDefs USING[ IsBound ],
InlineDefs USING[ BITAND, BITSHIFT, COPY, HighHalf, LowHalf ],
KeyDefs USING[ Keys ],
LookerDefs USING[ Clear, DiskChar, DiskCommit, DiskMultiple, DiskPos,
EraseChar, GetLength, Lookup, LookupOutcome,
SetPos, WriteChar, WriteMultiple, WriteTitle ],
MMOps USING[ MakeBoot ],
Mopcodes USING[ zLI2, zLI3, zMISC, zSTARTIO ],
OsStaticDefs USING[ OsStatics ],
--PrivateCommonPineDefs USING[pLeader],
ProcessDefs USING[ Abort, CV, InterruptLevel, SetPriority ],
PupDefs USING[ PupAddress ],
PupTypes,
StreamDefs USING[ GetDefaultKey, KeyboardHandle, StartKeyHandler ],
StringDefs,
SystemDefs USING[ AllocateHeapNode, FreeHeapNode ];
Looker: MONITOR
IMPORTS DisplayDefs, InlineDefs, FrameDefs, LookerDefs, MMOps, ProcessDefs,
StreamDefs, StringDefs, SystemDefs =
BEGIN
herald: STRING = "PupWatch: version of 24-Nov-81 14:49:32"L;
LongTicks: TYPE = LONG CARDINAL;
ClockFormat: TYPE = { AltoI, AltoII};
AltoClock: TYPE = MACHINE DEPENDENT RECORD[ -- hardware clock formats --
SELECT OVERLAID ClockFormat FROM
AltoI => [clock: CanonicalClock],
AltoII => [junk1: [0..17B], low10: [0..1777B], junk2: [0..3B], high16: CARDINAL],
ENDCASE ];
CanonicalClock: TYPE = MACHINE DEPENDENT RECORD[
SELECT OVERLAID * FROM
pieces => [low10: [0..1777B], zero: [0..77B], high16: CARDINAL],
tick => [ticks: LongTicks]
ENDCASE ];
longTicksPerSecond: LongTicks = 6321200B -- (5.88*10↑6/224)SHL 6 --;
longTicksPerMSec: LongTicks = longTicksPerSecond/1000;
ReadRawClock: PROC RETURNS[ AltoClock ] = MACHINE CODE
{ Mopcodes.zMISC, 11B; };
myClockFormat: ClockFormat;
MSec: PROC[rawClock: AltoClock] RETURNS[ LONG CARDINAL ] = INLINE
BEGIN
clock: CanonicalClock;
WITH raw: rawClock SELECT myClockFormat FROM
AltoI => clock ← raw.clock;
AltoII => clock ← [pieces[low10:raw.low10, zero:, high16:raw.high16]];
ENDCASE;
clock.zero ← 0;
RETURN[clock.ticks / longTicksPerMSec]
END;
-- Ethernet driver --
HostNumber: TYPE = [0..256);
maxEtherLength: CARDINAL = 532 + 22 -- max Pup data + Pup header --;
-- Beware: ether packets with maxEtherLength+4 bytes can cause a parity
-- error at location 600B on Alto I's (microcode bug).
EtherBufferData: TYPE = MACHINE DEPENDENT RECORD[
etherDest, etherSrce: HostNumber,
etherType: {pup(1000B), ois(3000B), (177777B)},
body: SELECT OVERLAID * FROM
raw => [data:PACKED ARRAY [0..maxEtherLength) OF CHARACTER],
pup => [pup: BufferData],
ENDCASE ];
EtherBuffer: TYPE = POINTER TO EtherBufferData;
-- driver uses two ether buffers: one current, one standby --
etherBuffers: POINTER TO ARRAY [1..2] OF EtherBufferData =
SystemDefs.AllocateHeapNode[2*SIZE[EtherBufferData]];
Ethernet: TYPE = MACHINE DEPENDENT RECORD[
status: EtherStatus,
interrupt: WORD,
endCount: CARDINAL,
loadMask: WORD,
inputCount: CARDINAL,
inputPointer: EtherBuffer,
outputCount: CARDINAL,
outputPointer: EtherBuffer,
spare2: [0..256),
hostNumber: HostNumber ];
EtherStatus: TYPE = MACHINE DEPENDENT RECORD[
micro: { inputDone(0), outputDone(1), overRun(2), overLoad(3),
zeroLength(4), reset(5), microError(6), unset(255) },
spare0, spare1, dataLate, collision, crc, input, output, incomplete:
{ yes(0), no(1) } ];
notPosted: EtherStatus = [unset,,,,,,,,];
inputOK: EtherStatus = [inputDone,no,no,no,no,no,no,no,no];
ethernet: POINTER TO Ethernet = LOOPHOLE[600B];
etherCond: CONDITION; -- for naked notify --
StartInput: PROC = MACHINE CODE
{ Mopcodes.zLI2; Mopcodes.zSTARTIO };
Reset: PROC = MACHINE CODE
{ Mopcodes.zLI3; Mopcodes.zSTARTIO };
-- Pup level --
maxPupLength: CARDINAL = 54;
BufferData: TYPE = MACHINE DEPENDENT RECORD[
pupLength: CARDINAL,
pupTransportControl: [0..256),
pupType: PupTypes.PupType,
pupID: PupTypes.Pair,
dest: PupDefs.PupAddress,
source: PupDefs.PupAddress,
pupBody: SELECT OVERLAID * FROM
pupChars => [pupChars: PACKED ARRAY[0..maxPupLength) OF CHARACTER],
pupBytes => [pupBytes: PACKED ARRAY[0..maxPupLength) OF [0..377B]],
pupWords => [pupWords: ARRAY[0..maxPupLength/2) OF CARDINAL],
pupString => [pupString: StringBody],
rfc => [address: PupDefs.PupAddress ],
ack => [maximumBytesPerPup, numberOfPupsAhead,
numberOfBytesAhead: CARDINAL ],
abort => [abortCode: CARDINAL,
abortText: PACKED ARRAY [0..0) OF CHARACTER ],
error => [errorHeader: ARRAY [0..9] OF WORD,
errorCode: CARDINAL,
errorOptions: WORD,
errorText: PACKED ARRAY [0..0) OF CHARACTER],
nameIs => [nameIs: ARRAY [0..0) OF PupDefs.PupAddress ],
ENDCASE ];
Buffer: TYPE = POINTER TO BufferData;
ContentsBytes: PROC[b: Buffer] RETURNS[CARDINAL] = INLINE
{ RETURN[ b.pupLength - 22 ] };
nBuffers: CARDINAL = 300;
BufferIndex: TYPE = [0..nBuffers);
buffers: POINTER TO ARRAY BufferIndex OF BufferData =
SystemDefs.AllocateHeapNode[nBuffers * SIZE[BufferData]];
times: POINTER TO ARRAY BufferIndex OF AltoClock =
SystemDefs.AllocateHeapNode[nBuffers * SIZE[AltoClock]];
lost: PACKED ARRAY BufferIndex OF BOOLEAN ← ALL[FALSE];
logged: PACKED ARRAY BufferIndex OF BOOLEAN ← ALL[FALSE];
wBuffer: BufferIndex ← 0; -- buffer being written by ethernet driver --
rBuffer: BufferIndex ← 0; -- buffer Looker wants to read --
fullBuffers: [0..nBuffers] ← 0;
bufferChange: CONDITION;
driverWaiting: BOOLEAN ← FALSE;
lookerWaiting: BOOLEAN ← FALSE;
allUsed: BOOLEAN ← FALSE; -- whether all buffers have been written --
GetBuffer: ENTRY PROC RETURNS[BufferIndex] = --INLINE--
BEGIN
ENABLE UNWIND => lookerWaiting ← FALSE;
WHILE fullBuffers = 0
DO lookerWaiting ← TRUE; WAIT bufferChange ENDLOOP;
lookerWaiting ← FALSE;
RETURN[ rBuffer ]
END;
ReturnBuffer: ENTRY PROC = --INLINE--
{ rBuffer ← IF rBuffer = LAST[BufferIndex] THEN 0 ELSE SUCC[rBuffer];
fullBuffers ← fullBuffers-1;
IF driverWaiting THEN NOTIFY bufferChange };
ReplayBuffers: ENTRY PROC =
BEGIN
rBuffer ← IF allUsed
THEN IF wBuffer = LAST[BufferIndex] THEN 0 ELSE SUCC[wBuffer]
ELSE 0;
fullBuffers ← (wBuffer+nBuffers-rBuffer) MOD nBuffers;
IF lookerWaiting THEN NOTIFY bufferChange;
END;
FlushBuffers: ENTRY PROC =
BEGIN
rBuffer ← wBuffer ← 0;
fullBuffers ← 0; allUsed ← FALSE; lost ← ALL[FALSE]; logged ← ALL[FALSE];
IF driverWaiting THEN NOTIFY bufferChange;
END;
NextBuffer: INTERNAL PROC = --INLINE--
BEGIN
ENABLE UNWIND => driverWaiting ← FALSE;
IF wBuffer = LAST[BufferIndex]
THEN { allUsed ← TRUE; wBuffer ← 0 }
ELSE wBuffer ← SUCC[wBuffer];
IF lookerWaiting THEN NOTIFY bufferChange;
logged[wBuffer] ← lost[wBuffer] ← FALSE;
-- don't overwrite nBuffers/2 old ones, to help "replay" facility. --
IF fullBuffers >= nBuffers/2
THEN WHILE fullBuffers+50 >= nBuffers/2 --make sure we have 50 free--
DO driverWaiting←TRUE; lost[wBuffer] ← TRUE;
WAIT bufferChange
ENDLOOP;
fullBuffers ← fullBuffers+1;
driverWaiting ← FALSE;
END;
wanted: PupDefs.PupAddress ← [ [0], [0], [0,0] ];
Driver: ENTRY PROC =
BEGIN
ProcessDefs.SetPriority[4];
ethernet.inputCount ← SIZE[EtherBufferData];
ethernet.inputPointer ← @(etherBuffers[1]);
ethernet.status ← notPosted;
StartInput[];
DO ENABLE ABORTED => EXIT;
current: EtherBuffer = ethernet.inputPointer;
next: EtherBuffer = IF current = @(etherBuffers[1])
THEN @(etherBuffers[2])
ELSE @(etherBuffers[1]);
thisStatus: EtherStatus;
DO WAIT etherCond;
IF (thisStatus ← ethernet.status) # notPosted
THEN BEGIN
-- restart the microcode as soon as possible --
ethernet.inputPointer ← next;
ethernet.status ← notPosted;
StartInput[];
EXIT
END;
ENDLOOP;
IF thisStatus = inputOK
AND current.etherType = pup
AND ( (current.pup.dest.host = wanted.host
AND current.pup.dest.net = wanted.net)
OR (current.pup.source.host = wanted.host
AND current.pup.source.net = wanted.net)
)
THEN BEGIN
myBuffer: BufferIndex = wBuffer;
times[myBuffer] ← ReadRawClock[];
InlineDefs.COPY[from: @(current.pup), to: @(buffers[myBuffer]),
nwords: SIZE[BufferData] ];
NextBuffer[];
END;
ENDLOOP;
Reset[];
ProcessDefs.SetPriority[1];
END;
driverProcess: PROCESS;
NoInterruptLevel: ERROR = CODE;
etherLevel: ProcessDefs.InterruptLevel;
TakeEthernet: PROC =
BEGIN
ethernet.hostNumber ← 0 --promiscuous--;
FOR i: ProcessDefs.InterruptLevel IN ProcessDefs.InterruptLevel
DO IF ProcessDefs.CV[i] = NIL THEN { etherLevel ← i; EXIT };
REPEAT
FINISHED => ERROR NoInterruptLevel[];
ENDLOOP;
ProcessDefs.CV[etherLevel] ← @etherCond;
ethernet.interrupt ← InlineDefs.BITSHIFT[1, etherLevel];
Reset[]; Reset[]; -- Hal says it sometimes doesn't work! --
driverProcess ← FORK Driver[];
END;
GiveEthernet: PROC =
BEGIN
ProcessDefs.Abort[driverProcess];
JOIN driverProcess;
ethernet.interrupt ← 0;
ProcessDefs.CV[etherLevel] ← NIL;
Reset[];
END;
-- Synchronization with user type-in --
inputActive: BOOLEAN ← TRUE;
lookerActive: BOOLEAN ← FALSE;
pauseWanted: BOOLEAN ← FALSE;
inputChange: CONDITION;
lookerChange: CONDITION;
ActivateLooker: ENTRY PROC RETURNS[ BOOLEAN ] = --INLINE--
BEGIN
ENABLE UNWIND => NULL;
IF inputActive OR pauseWanted
THEN BEGIN
WHILE inputActive OR pauseWanted DO WAIT inputChange ENDLOOP;
RETURN[FALSE]
END
ELSE BEGIN
lookerActive ← TRUE; RETURN[TRUE]
END;
END;
DeactivateLooker: ENTRY PROC = INLINE
{ lookerActive ← FALSE; IF inputActive THEN NOTIFY lookerChange };
ActivateInput: ENTRY PROC =
BEGIN
ENABLE UNWIND => NULL;
inputActive ← TRUE;
WHILE lookerActive DO WAIT lookerChange ENDLOOP;
END;
DeactivateInput: ENTRY PROC =
BEGIN
inputActive ← FALSE; NOTIFY inputChange;
END;
-- Output subroutines --
mode: { display, disk} ← display;
-- Display line layout is:
-- 7777: from 77#377#777 [aData,L:777,to:177777]abcdefghijklmn
-- 7777: to 77#377#777 [aData,L:777,to:177777]abcdefghijklmn
tabFrom: CARDINAL;
tabTo: CARDINAL;
tabAddr: CARDINAL;
tabPkt: CARDINAL;
tabData: CARDINAL;
-- Disk log line layout is:
-- 7777: from 377#377#377 [aData,L:1024,to:177777] abcdefghijklmn
-- 7777: to 377#377#377 [aData,L:1024,to:177777] abcdefghijklmn
diskFrom: CARDINAL;
diskPkt: CARDINAL;
diskData: CARDINAL;
InitTabs: PROC =
BEGIN
tabFrom ← LookerDefs.GetLength["7777: "L];
tabTo ← tabFrom +
(LookerDefs.GetLength["from"L]-LookerDefs.GetLength["to"L]);
tabAddr ← tabFrom + LookerDefs.GetLength["from "L];
tabPkt ← tabAddr + LookerDefs.GetLength["77#777#777 "L];
tabData ← tabPkt + LookerDefs.GetLength["[aData,L:777,to:177777]"L];
diskFrom ← 6; -- "7777: " --
diskPkt ← diskFrom + 17; -- "from 377#377#377 " --
diskData ← diskPkt + 25; -- "[aData,L:1024,to:177777] " --
END;
WriteChar: PROC[CHARACTER] ← LookerDefs.WriteChar;
WriteMultiple: PROC[DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] ←
LookerDefs.WriteMultiple;
WriteString: PROC[s: STRING] =
{ WriteMultiple[DESCRIPTOR[@(s.text),s.length]] };
WritePup: PROC[b: Buffer] = INLINE
{ WriteMultiple[DESCRIPTOR[@(b.pupChars),
MIN[ContentsBytes[b],maxPupLength]]] };
WriteOctal777: PROC[n: CARDINAL] =
BEGIN
IF n >= 10B
THEN BEGIN
IF n >= 100B
THEN WriteChar['0 + n / 100B];
WriteChar['0 + (n/10B) MOD 10B];
END;
WriteChar['0 + n MOD 10B];
END;
WriteOctal: PROC[n: CARDINAL] = INLINE
BEGIN
IF n < 1000B
THEN WriteOctal777[n]
ELSE WriteFullOctal[n];
END;
WriteFullOctal: PROC[n: CARDINAL] =
BEGIN
WriteOctal777[n/1000B];
WriteChar['0 + ((n/100B) MOD 10B)];
WriteChar['0 + ((n/10B) MOD 10B)];
WriteChar['0 + n MOD 10B];
END;
WriteLongOctal: PROC[n: LONG CARDINAL] = INLINE
BEGIN
IF InlineDefs.HighHalf[n] # 0 THEN WriteOctal[InlineDefs.HighHalf[n]];
WriteOctal[InlineDefs.LowHalf[n]];
END;
WritePair: PROC[p: PupTypes.Pair] =
{ WriteOctal[p.a]; WriteChar[',]; WriteOctal[p.b] };
WriteDecimal: PROC[n: CARDINAL] = INLINE
{ s: STRING = [5]; StringDefs.AppendDecimal[s,n]; WriteString[s] };
WriteAddr: PROC[addr: PupDefs.PupAddress] = INLINE
BEGIN
WriteOctal777[addr.net];
WriteChar['#];
WriteOctal777[addr.host];
WriteChar['#];
WriteOctal777[InlineDefs.BITAND[addr.socket.b,777B]];
END;
WriteID: PROC[n: CARDINAL] = INLINE
BEGIN
WriteString["to:"];
WriteOctal[n];
END;
prevMS: LONG CARDINAL ← MSec[ReadRawClock[]];
-- layout of Pup id's for Sequin/Leaf protocol --
SequinID: TYPE = MACHINE DEPENDENT RECORD[
allocate: Byte, receiveSequence: Byte,
control: SequinControl, sendSequence: Byte ];
Byte: TYPE = [0..256);
SequinControl: TYPE = MACHINE DEPENDENT {
data(0), ack(1), nop(2), restart(3),
check(4), open(5), break(6), close(7),
closed(8), destroy(9), dallying(10), quit(11),
broken(12), retransmit(13), stifle(14), openclose(15),
opendestroy(16), (255) };
leafSocket: PupTypes.PupSocketID = [a:0,b:43B];
Watch: PROC[ b: Buffer, time: AltoClock ] =
BEGIN
DefaultBodyPrintout: PROC[b: Buffer] = INLINE
{ WriteString["L:"]; WriteOctal[ContentsBytes[b]] };
BEGIN
mscSrv: BOOLEAN = b.source.socket = PupTypes.miscSrvSoc
OR b.dest.socket = PupTypes.miscSrvSoc;
leaf: BOOLEAN = b.source.socket = leafSocket
OR b.dest.socket = leafSocket;
newMS: LONG CARDINAL = MSec[time];
IF newMS >= prevMS + 10000
THEN IF newMS >= prevMS + 1000*LONG[1000] -- 1000 seconds --
THEN WriteString[IF prevMS = 0 THEN "first"L ELSE "long"L]
ELSE BEGIN
WriteDecimal[InlineDefs.LowHalf[(newMS-prevMS)/1000]];
WriteChar['s];
END
ELSE WriteDecimal[InlineDefs.LowHalf[ (newMS-prevMS) MOD 10000 ]];
prevMS ← newMS;
WriteChar[':];
IF mode = display
THEN BEGIN
IF b.source.host = wanted.host
AND b.source.net = wanted.net
THEN { LookerDefs.SetPos[tabTo]; WriteString["to"];
LookerDefs.SetPos[tabAddr]; WriteAddr[b.dest] }
ELSE { LookerDefs.SetPos[tabFrom]; WriteString["from"];
LookerDefs.SetPos[tabAddr]; WriteAddr[b.source] };
LookerDefs.SetPos[tabPkt];
END
ELSE BEGIN
LookerDefs.DiskPos[diskFrom];
IF b.source.host = wanted.host
AND b.source.net = wanted.net
THEN { WriteString[" to "]; WriteAddr[b.dest] }
ELSE { WriteString["from "]; WriteAddr[b.source] };
LookerDefs.DiskPos[diskPkt];
END;
WriteChar['[];
BEGIN
type: STRING = SELECT b.pupType FROM
-- registered pup types
echoMe => "echoMe",
iAmEcho => "iAmEcho",
badEcho => "badEcho",
error => "error",
rfc => "rfc",
abort => "abort",
end => "end",
endRep => "endRep",
data => "data",
aData => "aData",
ack => "ack",
mark => "mark",
int => "int",
intRep => "intRep",
aMark => "aMark",
eData => "eData",
eAck => "eAck",
eEnd => "eEnd",
eAbort => "eAbort",
rpp => "rpp",
-- Cedar RPC pup types. Bits are:
-- 8..10: 3 (=> start at pt140)
-- 11: {end(0),notEnd(1)}
-- 12: {dontAck(0),pleaseAck(1)}
-- 13..15: {call,data,ack,nak,rfa}
pt140 => "RPC-call-end",
pt141 => "RPC-data-end",
pt142 => "RPC-ack",
pt143 => "RPC-nak",
pt144 => "RPC-RFA",
pt150 => "RPC-a-call-end",
pt151 => "RPC-a-data-end",
pt152 => "RPC-ping",
pt153 => "RPC-a-nak",
pt154 => "RPC-a-RFA",
pt160 => "RPC-call-more",
pt161 => "RPC-data-more",
pt170 => "RPC-a-call-more",
pt171 => "RPC-a-data-more",
-- unregistered pup types (possible overlap)
gatewayRequest => "gatewayRequest",
--also dateTextRequest (socket 4), statisticsRequest (socket 22)
gatewayInfo => "gatewayInfo",
--also dateTextIs (socket 4), statisticsAre (socket 22)
dateTenexRequest => "dateTenexRequest",
dateTenexIs => "dateTenexIs",
dateAltoRequest => "dateAltoRequest",
dateAltoIs => "dateAltoIs",
mailCheck => "mailCheck",
mailIsNew => "mailIsNew",
mailNotNew => "mailNotNew",
mailError => "mailError",
mailCheckLaurel => "mailCheckL",
nameLookup => "nameLookup",
nameIs => "nameIs",
nameError => "nameError",
addressLookup => "addrLookup",
addressIs => "addrIs",
whereIsUser => "whereIsUser",
userIs => "userIs",
userError => "userError",
netDirVersion => "netDirVersion",
--also eventReport (socket 30)
sendNetDir => "sendNetDir",
--also eventReportReply (socket 30)
bootFileSend => "bootFileSend",
kissOfDeath => "kissOfDeath",
bootDirReq => "bootDirReq",
bootDirReply =>
IF NOT leaf THEN "bootDirReply"
ELSE SELECT LOOPHOLE[b.pupID,SequinID].control FROM
data=> "lData", ack=> "lAck",
nop=> "nop", restart=> "restart",
check=> "check", open=> "open",
break=> "break", close=> "close",
closed=>"closed", destroy=> "destroy",
dallying=>"dallying", quit=> "quit",
broken=>"broken", retransmit=> "retransmit",
stifle=>"stifle", openclose=> "openClose",
opendestroy=>"opendestroy",
ENDCASE => "leaf?",
-- Pine pup types
request => IF mscSrv THEN "userAuthReq"
ELSE "request",
result => IF mscSrv THEN "userAuthOk"
ELSE "result",
unsolicited => IF mscSrv THEN "userAuthBad"
ELSE "unsolicited",
custodian => "custodian",
sync => "sync",
pineAck => "pineAck",
noop => "noop",
ENDCASE => NIL;
IF type = NIL
THEN BEGIN
WriteString["pt"];
WriteOctal[LOOPHOLE[b.pupType, CARDINAL]];
END
ELSE WriteString[type];
END;
WriteChar[',];
SELECT b.pupType FROM
error =>
SELECT b.errorCode FROM
PupTypes.badChecksumPupErrorCode =>
WriteString["badChecksum"];
PupTypes.noProcessPupErrorCode =>
WriteString["noSuchPort"];
PupTypes.resourceLimitsPupErrorCode =>
WriteString["resourceLimits"];
PupTypes.inconsistentPupErrorCode =>
WriteString["inconsistentPup"];
PupTypes.cantGetTherePupErrorCode =>
WriteString["cantGetThere"];
PupTypes.hostDownPupErrorCode =>
WriteString["hostDown"];
PupTypes.eightHopsPupErrorCode =>
WriteString["sixteenHops"];
PupTypes.tooBigPupErrorCode =>
WriteString["tooBigPup"];
PupTypes.iAmNotAGatewayPupErrorCode =>
WriteString["iAmNotAGateway"];
PupTypes.gatewayResourceLimitsPupErrorCode =>
WriteString["gatewayResources"];
ENDCASE => WriteOctal[b.errorCode];
mailCheck, mailCheckLaurel, nameLookup =>
WritePup[b];
nameIs =>
WriteAddr[b.nameIs[0]];
abort =>
WriteOctal[b.abortCode];
data, aData =>
BEGIN
l: CARDINAL = ContentsBytes[b];
WriteID[b.pupID.b+l];
WriteString[",L:"];
WriteOctal[l];
END;
ack =>
BEGIN
WriteID[b.pupID.b];
WriteString[",pups:"];
WriteOctal777[b.numberOfPupsAhead];
END;
mark, aMark =>
BEGIN
WriteID[b.pupID.b+1];
WriteString[",mk:"];
WriteOctal777[b.pupBytes[0]];
END;
rfc, echoMe =>
BEGIN
wellKnown: STRING = SELECT b.dest.socket FROM
PupTypes.telnetSoc => "telnet",
PupTypes.gatewaySoc => "gateway",
PupTypes.ftpSoc => "ftp",
PupTypes.miscSrvSoc => "miscSrv",
PupTypes.echoSoc => "echo",
PupTypes.bspTestSoc => "bspTest",
PupTypes.mailSoc => "mail",
PupTypes.eftpReceiveSoc => "eftp",
PupTypes.copyDiskSoc => "copyDisk",
PupTypes.rpcpSoc => "rpcp",
PupTypes.librarianSoc => "librarian",
PupTypes.pineSoc => "pine",
ENDCASE => NIL;
IF wellKnown # NIL
THEN WriteString[wellKnown]
ELSE BEGIN
IF b.dest.socket.b IN [50B..57B]
AND b.dest.socket.a IN [0..1]
THEN BEGIN -- RFC or EchoMe on a Grapevine socket --
gvSoc: STRING = SELECT b.dest.socket.b FROM
50B => "RS-enquire",
51B => "RS-update",
52B => "RS-poll",
53B => "GV-Lily",
54B => "MS-poll",
55B => "MS-forward",
56B => "MS-send",
57B => "MS-retrieve",
ENDCASE => ERROR;
IF b.dest.socket.a = 1 THEN WriteString["Test-"];
WriteString[gvSoc];
END
ELSE IF b.dest.socket.a = 0
AND b.dest.socket.b IN [0 .. 256)
THEN WriteOctal777[b.dest.socket.b];
END;
END;
end, endRep, int, intRep => NULL;
pt140,pt141,pt142,pt143,pt144,
pt150,pt151,pt152,pt153,pt154,
pt160,pt161,
pt170,pt171 => --Cedar RPC packet--
BEGIN
l: CARDINAL = ContentsBytes[b];
WriteString["W:"];
IF l < 20
THEN { WriteString["?-"L]; WriteOctal[10-l/2] }
ELSE WriteOctal[l/2-10];
END;
addressLookup =>
WriteAddr[b.nameIs[0]];
bootDirReply =>
IF leaf
THEN BEGIN
id: SequinID = LOOPHOLE[b.pupID];
IF b.dest.socket = leafSocket
THEN BEGIN
WriteString["rec:"];
WriteOctal777[id.receiveSequence];
WriteString[",send:"];
WriteOctal777[id.sendSequence];
END
ELSE BEGIN
WriteString["send:"];
WriteOctal777[id.sendSequence];
WriteString[",rec:"];
WriteOctal777[id.receiveSequence];
END;
-- WriteString[",alloc:"];
-- WriteOctal777[id.allocate];
IF id.control = data
THEN BEGIN
l: CARDINAL = ContentsBytes[b];
WriteString[",L:"];
WriteOctal[l];
END;
END;
request =>
IF mscSrv THEN WriteString[@(b.pupString)] --userAuthReq--
ELSE {
-- Pine stuff commented out to save space - otherwise the compiler runs out
-- Wr: PROC[s: STRING] = { WriteString[s] };
-- L: PrivateCommonPineDefs.pLeader = LOOPHOLE[@b.pupBody];
-- P: POINTER TO CommonPineDefs.Parameters = LOOPHOLE[@L.params];
-- WITH R: P.requestP SELECT FROM
-- ReadPage => { Wr["ReadPage "]; WriteOctal[R.pageNumber] };
-- WritePage => { Wr["WritePage "]; WriteOctal[R.pageNumber] };
-- SetLength => { Wr["SetLength "]; WriteLongOctal[R.byteCount] };
-- ReadLength => Wr["ReadLength"];
-- CloseFile => Wr["CloseFile"];
-- DestroyAnonymousFile => Wr["DestroyAnonymousFile"];
-- ReadData => { Wr["ReadData "]; WriteLongOctal[R.firstBytePosition];
-- WriteString[" for "]; WriteOctal[R.byteCount] };
-- WriteData => { Wr["WriteData "]; WriteLongOctal[R.firstBytePosition];
-- WriteString[" for "]; WriteOctal[R.byteCount] };
-- ReadAttribute => { Wr["ReadAttribute "]; WriteOctal[R.attributeNumber] };
-- WriteAttribute => { Wr["WriteAttribute "]; WriteOctal[R.attributeNumber] };
-- SetReadLock => Wr["SetReadLock"];
-- SetWriteLock => Wr["SetWriteLock"];
-- ReleaseReadLock => Wr["ReleaseReadLock"];
-- ObtainCapability => Wr["ObtainCapability"];
-- Room => Wr["Room"];
-- ChangePassword => Wr["ChangePassword"];
-- OpenFile => Wr["OpenFile"];
-- CreateAnonymousFile => Wr["CreateAnonymousFile"];
-- FindFile => Wr["FindFile"];
-- LockQuery => Wr["LockQuery"];
-- TransCompletionQuery => Wr["TransCompletionQuery"];
-- OpenFileFromCapability => Wr["OpenFileFromCapability"];
-- Logout => Wr["Logout"];
-- CloseTransaction => Wr["CloseTransaction"];
-- AbortTransaction => Wr["AbortTransaction"];
-- LookupFile => Wr["LookupFile"];
-- CreateFile => Wr["CreateFile"];
-- DestroyFile => Wr["DestroyFile"];
-- RenameFile => Wr["RenameFile"];
-- NextFile => Wr["NextFile"];
-- NextFewFiles => Wr["NextFewFiles"];
-- ValidateYourLocks => Wr["ValidateYourLocks"];
-- ReleaseYourReadLocks => Wr["ReleaseYourReadLocks"];
-- ENDCASE => Wr["Unknown Request Variant"];
-- IF P.requestP.code IN CommonPineDefs.DataRequest THEN {
-- Wr[" sfh "]; WriteOctal[P.h] };
};
result =>
IF mscSrv THEN DefaultBodyPrintout[b] --userAuthOk--
ELSE {
-- More Pine stuff commented out ...
-- Wr: PROC[s: STRING] = { WriteString[s] };
-- L: PrivateCommonPineDefs.pLeader = LOOPHOLE[@b.pupBody];
-- P: POINTER TO CommonPineDefs.Parameters = LOOPHOLE[@L.params];
-- WITH R: P.resultP SELECT FROM
-- HeresData => { Wr["HeresData "]; WriteOctal[R.length] };
-- HeresLFH => Wr["HeresLFH"];
-- HeresEntry => Wr["HeresEntry"];
-- HeresFileList => { Wr["HeresFileList "]; WriteOctal[R.count] };
-- HeresFile => { Wr["HeresFile "]; WriteOctal[R.f] };
-- HeresLength => { Wr["HeresLength "]; WriteLongOctal[R.length] };
-- CommandAck => Wr["CommandAck"];
-- CommandNak => { Wr["CommandNak "]; WriteOctal[LOOPHOLE[R.en]] };
-- LoginResponse => Wr["LoginResponse"];
-- TransactionClosed => Wr["TransactionClosed"];
-- LogoutResponse => Wr["LogoutResponse"];
-- ResourceData => Wr["ResourceData"];
-- TransCompletionInfo => Wr["TransCompletionInfo"];
-- HeresRoom => Wr["HeresRoom"];
-- LockQueryResponse => Wr["LockQueryResponse"];
-- ENDCASE => Wr["Unknown Result Variant"];
};
unsolicited =>
IF mscSrv THEN DefaultBodyPrintout[b] --userAuthBad--
ELSE {
-- More Pine stuff commented out ...
-- L: PrivateCommonPineDefs.pLeader = LOOPHOLE[@b.pupBody];
-- P: POINTER TO CommonPineDefs.Parameters = LOOPHOLE[@L.params];
-- WITH R: P.unsolicitedP SELECT FROM
-- TransactionAborted => WriteString["TransactionAborted"];
-- ReadLockBroken => WriteString["ReadLockBroken"];
-- ENDCASE => WriteString["Unknown Unsolicited Variant"];
};
custodian => {
-- More Pine stuff commented out ...
-- L: PrivateCommonPineDefs.pLeader = LOOPHOLE[@b.pupBody];
-- P: POINTER TO CommonPineDefs.Parameters = LOOPHOLE[@L.params];
-- WITH R: P.custodianP SELECT FROM
-- Login => WriteString["Login"];
-- AddServer => WriteString["AddServer"];
-- ResourceLocation => WriteString["ResourceLocation"];
-- ENDCASE => WriteString["Unknown Custodian Variant"];
};
sync => NULL;
pineAck => NULL;
noop => NULL;
ENDCASE => DefaultBodyPrintout[b];
WriteChar[']];
IF mode = disk
THEN BEGIN
LookerDefs.DiskPos[diskData];
WriteString["ID="];
WritePair[b.pupID];
WriteString[" from="];
WritePair[LOOPHOLE[b.source.socket]];
WriteString[" to="];
WritePair[LOOPHOLE[b.dest.socket]];
BEGIN
l: CARDINAL = MIN[ContentsBytes[b],maxPupLength];
WriteChar[Ascii.CR];
LookerDefs.DiskPos[diskData];
IF l = 0 THEN WriteString["{empty}"] ELSE WritePup[b];
WriteChar[Ascii.CR];
LookerDefs.DiskPos[diskData];
IF l = 0
THEN WriteString["{empty}"]
ELSE FOR i: CARDINAL IN [0..MIN[l,maxPupLength/2])
DO WriteOctal777[b.pupBytes[i]];
WriteChar[Ascii.SP]
ENDLOOP;
END;
END
ELSE IF b.pupType = data OR b.pupType = aData
THEN { LookerDefs.SetPos[tabData]; WritePup[b] };
WriteChar[Ascii.CR];
END;
END;
speed: {slow, fast} ← fast;
lookerCount: CARDINAL;
LookerMain: PROC =
BEGIN
countLimit: CARDINAL = 40;
ProcessDefs.SetPriority[0];
DO ENABLE ABORTED => EXIT;
this: BufferIndex = GetBuffer[];
IF ActivateLooker[]
THEN BEGIN
IF lost[this]
THEN { WriteString["Lost packet(s)"L]; WriteChar[Ascii.CR] };
Watch[@(buffers[this]), times[this]];
ReturnBuffer[];
IF speed = slow
AND (lookerCount ← lookerCount + 1) >= countLimit
THEN { WriteString["Type <SP> to continue ..."L];
pauseWanted ← TRUE };
DeactivateLooker[];
END;
ENDLOOP;
GiveEthernet[];
END;
WriteDiskLog: PROC =
BEGIN
WriteChar ← LookerDefs.DiskChar;
WriteMultiple ← LookerDefs.DiskMultiple;
mode ← disk;
WriteChar[Ascii.CR];
ReplayBuffers[];
IF logged[GetBuffer[]]
THEN BEGIN
WriteString["{ continued from previous logged packets }"L];
WriteChar[Ascii.CR];
THROUGH [1..fullBuffers]
DO this: BufferIndex = GetBuffer[];
IF logged[this]
THEN prevMS ← MSec[times[this]]
ELSE EXIT;
ReturnBuffer[];
ENDLOOP;
END
ELSE BEGIN
WriteString["Watching host "L];
WriteOctal777[wanted.net]; WriteChar['#];
WriteOctal777[wanted.host]; WriteChar['#];
WriteChar[Ascii.CR];
END;
THROUGH [1..fullBuffers]
DO this: BufferIndex = GetBuffer[];
IF lost[this]
THEN { WriteString["Lost packet(s)"L]; WriteChar[Ascii.CR] };
Watch[@(buffers[this]), times[this]];
logged[this] ← TRUE;
ReturnBuffer[];
ENDLOOP;
WriteChar ← LookerDefs.WriteChar;
WriteMultiple ← LookerDefs.WriteMultiple;
mode ← display;
LookerDefs.DiskCommit[];
END;
-- user command input --
WriteTitle: PROC =
BEGIN
myTitle: STRING = [50];
StringDefs.AppendString[myTitle, "PUPWatch: watching host "L];
StringDefs.AppendNumber[myTitle, wanted.net, 8];
StringDefs.AppendChar[myTitle, '#];
StringDefs.AppendNumber[myTitle, wanted.host, 8];
StringDefs.AppendChar[myTitle, '#];
LookerDefs.WriteTitle[myTitle];
END;
HandleInput: PROC =
BEGIN
c: CHARACTER ← 'h;
DO alreadyPaused: BOOLEAN = pauseWanted;
IF pauseWanted AND c # Ascii.SP THEN WriteChar[Ascii.CR];
lookerCount ← 0;
SELECT c FROM
'q, 'Q => { WriteString["Quit"]; EXIT };
'f, 'F =>
BEGIN
WriteString["Fast"L];
speed ← fast; pauseWanted ← FALSE;
END;
'h, 'H =>
BEGIN
s: STRING = [40];
WriteString["Host (NLS-name or Net-address): "];
DO c: CHARACTER = key.get[key];
SELECT c FROM
Ascii.BS =>
BEGIN
IF s.length > 0
THEN { s.length ← s.length-1;
LookerDefs.EraseChar[s[s.length]] };
LOOP
END;
Ascii.ControlW =>
BEGIN
FOR i: CARDINAL DECREASING IN [0..s.length)
DO LookerDefs.EraseChar[s[i]] ENDLOOP;
s.length ← 0;
LOOP
END;
Ascii.DEL => GOTO del;
Ascii.SP, Ascii.CR, Ascii.ESC => EXIT;
ENDCASE => NULL;
IF s.length < s.maxlength
THEN { StringDefs.AppendChar[s, c]; WriteChar[c] };
ENDLOOP;
WriteString[" ... "];
GiveEthernet[]; -- stops driver --
BEGIN
outcome: LookerDefs.LookupOutcome;
net, host: [0..255];
[outcome, net, host] ← LookerDefs.Lookup[s];
SELECT outcome FROM
ok =>
BEGIN
WriteString["ok"L];
wanted.net ← [net]; wanted.host ← [host];
LookerDefs.Clear[];
FlushBuffers[];
WriteTitle[];
pauseWanted ← FALSE;
END;
noResponse => WriteString["no name-lookup response"L];
badName => WriteString["name not found"L];
ENDCASE => ERROR;
END;
TakeEthernet[]; -- starts driver --
EXITS
del => WriteString[" XXX"L];
END;
'r, 'R =>
BEGIN
LookerDefs.Clear[];
WriteString["Replay (Slow)"L];
ReplayBuffers[]; speed ← slow;
pauseWanted ← FALSE;
END;
's, 'S =>
BEGIN
WriteString["Slow"L];
speed ← slow;
pauseWanted ← FALSE;
END;
'w, 'W =>
IF version = boot
THEN WriteString["""Write log"" not available in Pupwatch.boot"L]
ELSE BEGIN
WriteString["Writing Pupwatch.log ... "L];
WriteDiskLog[];
WriteString["ok"L];
END;
Ascii.SP =>
IF pauseWanted
THEN { WriteString[" continuing"L]; pauseWanted ← FALSE }
ELSE pauseWanted ← TRUE;
'? =>
BEGIN
WriteString["Commands are: Fast, Host, Quit, Replay, Slow"];
IF version # boot THEN WriteString[", Write-log"L];
IF NOT pauseWanted
THEN { WriteChar[Ascii.CR]; WriteString["Type <SP> to pause"L] };
END;
ENDCASE => WriteChar['?];
IF NOT pauseWanted OR alreadyPaused THEN WriteChar[Ascii.CR];
IF pauseWanted THEN WriteString["Type <SP> to continue ..."L];
DeactivateInput[];
c ← key.get[key];
ActivateInput[];
ENDLOOP;
END;
-- Initialisation --
version: { bcd, image, boot } = SELECT TRUE FROM
FrameDefs.IsBound[MMOps.MakeBoot] => boot,
FrameDefs.IsBound[DisplayDefs.DestroyDisplay] => bcd,
ENDCASE => image;
lookerProcess: PROCESS;
key: StreamDefs.KeyboardHandle;
-- Our display module hasn't been started yet --
SELECT version FROM
image => { StreamDefs.StartKeyHandler[]; STOP };
bcd => { DisplayDefs.DestroyDisplay[] };
boot => { IF KeyDefs.Keys.Spare1=up THEN MMOps.MakeBoot[] };
ENDCASE => ERROR;
-- We're now fully running, possibly after ether-booting --
myClockFormat ←
SELECT OsStaticDefs.OsStatics.AltoVersion.engineeringnumber FROM
2,3 => AltoII,
ENDCASE => AltoI;
LookerDefs.WriteTitle[herald];
key ← StreamDefs.GetDefaultKey[];
InitTabs[];
lookerProcess ← FORK LookerMain[];
TakeEthernet[];
HandleInput[];
ProcessDefs.Abort[lookerProcess]; JOIN lookerProcess;
SystemDefs.FreeHeapNode[etherBuffers];
SystemDefs.FreeHeapNode[buffers];
SystemDefs.FreeHeapNode[times];
END.