Looker.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Russ Atkinson, July 25, 1984 11:33:35 pm PDT
Andrew Birrell October 25, 1983 11:29 am
DIRECTORY
Ascii USING[ CR, SP ],
BasicTime USING[ Pulses, PulsesToMicroseconds ],
BufferDefs USING[ Buffer, PupBuffer ],
DriverDefs USING[ ChangeNumberOfInputBuffers, GetDeviceChain ],
IO USING[ PutFR ],
LookerDefs USING[ Clear, DiskChar, DiskCommit, DiskMultiple, DiskPos, GetLength, InputAction, NotePausing, NoteSlow, ScreenLines, SendNow, SetPos, DisplayChar, DisplayMultiple, WriteTitle ],
PrincOpsUtils USING[ BITAND, IsBound, LongCOPY ],
Process USING[ Abort, EnableAborts, Priority, priorityBackground, SetPriority ],
PupDefs USING[ AnyLocalPupAddress, GetPupAddress, PupAddress, PupNameTrouble, PupPackageMake ],
PupTypes,
Rope USING[ Length, ROPE ],
RPCWatch USING[ SetSpyProc ],
SpecialCommunication USING[ SetEthernetOneListener, SetSpyProc ],
TeledebugProtocol,
VM USING[ AddressForPageNumber, Allocate, Free, Interval, PagesForWords ];
Looker: MONITOR
IMPORTS BasicTime, DriverDefs, IO, LookerDefs, PrincOpsUtils, Process, PupDefs, Rope, RPCWatch, SpecialCommunication, VM
EXPORTS LookerDefs
SHARES BufferDefs = {
MSec: PROC[rawClock: BasicTime.Pulses] RETURNS[ LONG CARDINAL ] = INLINE
{ RETURN[BasicTime.PulsesToMicroseconds[rawClock]/1000] };
Pup level --
bufferSize: CARDINAL;
maxPupLength: CARDINAL;
bigPupLength: CARDINAL = PupTypes.maxDataBytesPerGatewayPup;
smallPupLength: 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
big => [bigChars: PACKED ARRAY[0..bigPupLength) OF CHARACTER],
small => [smallChars: PACKED ARRAY[0..smallPupLength) OF CHARACTER],
pupChars => [pupChars: PACKED ARRAY[0..0) OF CHARACTER],
pupBytes => [pupBytes: PACKED ARRAY[0..0) OF [0..377B]],
pupWords => [pupWords: ARRAY[0..0) 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: PupTypes.PupErrorCode,
errorOptions: WORD,
errorText: PACKED ARRAY [0..0) OF CHARACTER],
nameIs => [nameIs: ARRAY [0..0) OF PupDefs.PupAddress ],
ENDCASE ];
Buffer: TYPE = LONG POINTER TO BufferData;
ContentsBytes: PROC[b: Buffer] RETURNS[CARDINAL] = INLINE
{ RETURN[ b.pupLength - 22 ] };
nBuffers: CARDINAL = 300;
BufferIndex: TYPE = [0..nBuffers);
bufferSpace: VM.Interval;
buffers: LONG POINTER TO --ARRAY BufferIndex OF-- BufferData ← NIL;
times: REF ARRAY BufferIndex OF BasicTime.Pulses ← NIL;
lost: PACKED ARRAY BufferIndex OF BOOLALL[FALSE];
logged: PACKED ARRAY BufferIndex OF BOOLALL[FALSE];
wBuffer: BufferIndex ← 0; -- buffer being written by ethernet driver --
rBuffer: BufferIndex ← 0; -- buffer Looker wants to read --
fullBuffers: [0..nBuffers] ← 0;
bufferChange: CONDITION;
lookerWaiting: BOOLFALSE;
allUsed: BOOLFALSE; -- whether all buffers have been written --
AllocBuffers: ENTRY PROC[big: BOOL] =
{
IF buffers # NIL THEN FreeBuffers[];
bufferSize ← IF big THEN SIZE[big BufferData] ELSE SIZE[small BufferData];
bufferSpace ← VM.Allocate[VM.PagesForWords[LONG[nBuffers] * bufferSize]];
buffers ← VM.AddressForPageNumber[bufferSpace.page];
maxPupLength ← IF big THEN bigPupLength ELSE smallPupLength;
times ← NEW[ARRAY BufferIndex OF BasicTime.Pulses];
InnerFlush[];
};
FreeBuffers: PROC = {
VM.Free[bufferSpace];
times ← NIL;
};
GetBuffer: ENTRY PROC RETURNS[BufferIndex] = {
ENABLE UNWIND => lookerWaiting ← FALSE;
WHILE fullBuffers = 0
DO lookerWaiting ← TRUE; WAIT bufferChange ENDLOOP;
lookerWaiting ← FALSE;
RETURN[ rBuffer ]
};
ReturnBuffer: ENTRY PROC = {
rBuffer ← IF rBuffer = LAST[BufferIndex] THEN 0 ELSE SUCC[rBuffer];
fullBuffers ← fullBuffers-1;
NOTIFY bufferChange;
};
ReplayBuffers: ENTRY PROC = {
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;
};
FlushBuffers: ENTRY PROC = { InnerFlush[] };
InnerFlush: INTERNAL PROC = {
rBuffer ← wBuffer ← 0;
fullBuffers ← 0; allUsed ← FALSE; lost ← ALL[FALSE]; logged ← ALL[FALSE];
waitingForBuffers ← FALSE;
NOTIFY bufferChange;
};
waitingForBuffers: BOOLFALSE;
NextBuffer: INTERNAL PROC = {
IF fullBuffers >= nBuffers/2
OR ( waitingForBuffers AND fullBuffers+50 >= nBuffers/2 --make sure we have 50 free--)
THEN { waitingForBuffers ← TRUE; lost[wBuffer] ← TRUE; RETURN };
waitingForBuffers ← FALSE;
IF wBuffer = LAST[BufferIndex]
THEN { allUsed ← TRUE; wBuffer ← 0 }
ELSE wBuffer ← SUCC[wBuffer];
fullBuffers ← fullBuffers+1;
IF lookerWaiting THEN NOTIFY bufferChange;
logged[wBuffer] ← lost[wBuffer] ← FALSE;
};
wanted: PupDefs.PupAddress;
myNet: CARDINAL ← 0;
broadcast: BOOLFALSE;
ShowBroadcast: PUBLIC --ENTRY-- PROC[wanted: BOOL] = {
broadcast ← wanted;
};
Driver: ENTRY PROC[b: BufferDefs.Buffer] RETURNS[BOOL] = {
IF b.encapsulation.ethernetType = pup
AND ( ( (b.dest.host = wanted.host OR (broadcast AND b.dest.host = 0) OR wanted.host=0)
AND ( b.dest.net = wanted.net
OR ( b.dest.net = 0 AND wanted.net = myNet )
OR wanted.net = 0
)
)
OR ( ( b.source.host = wanted.host OR wanted.host = 0 )
AND ( b.source.net = wanted.net
OR ( b.source.net = 0 AND wanted.net = myNet )
OR wanted.net = 0
)
)
)
THEN {
myBuffer: BufferIndex = wBuffer;
times[myBuffer] ← b.time;
PrincOpsUtils.LongCOPY[from: @b.bufferBody, to: buffers + myBuffer * bufferSize,
nwords: MIN[ (b.pupLength+1)/2, bufferSize ] ];
NextBuffer[];
};
RETURN[TRUE]--pkt still belongs to dispatcher--
};
RPCDriver: SAFE PROC[b: BufferDefs.PupBuffer] = TRUSTED {
[] ← Driver[LOOPHOLE[b]];
TEMP: patch to prevent RPCRuntime getting confused --
IF b.dest.host # 0 AND b.dest.host # realHost THEN b.pupType ← LOOPHOLE[0];
};
realHost: CARDINAL;
TakeEthernet: ENTRY PROC = {
realHost ← DriverDefs.GetDeviceChain[].hostNumber;
DriverDefs.ChangeNumberOfInputBuffers[TRUE];
IF NOT SpecialCommunication.SetEthernetOneListener[
physicalOrder: 1, newHostNumber: 0--promiscuous--]
THEN ERROR;
SpecialCommunication.SetSpyProc[Driver];
IF PrincOpsUtils.IsBound[RPCWatch.SetSpyProc] THEN RPCWatch.SetSpyProc[RPCDriver];
};
GiveEthernet: ENTRY PROC = {
DriverDefs.ChangeNumberOfInputBuffers[FALSE];
IF NOT SpecialCommunication.SetEthernetOneListener[
physicalOrder: 1, newHostNumber: realHost]
THEN ERROR;
SpecialCommunication.SetSpyProc[NIL];
IF PrincOpsUtils.IsBound[RPCWatch.SetSpyProc] THEN RPCWatch.SetSpyProc[NIL];
};
Synchronization with user type-in --
inputActive: BOOLFALSE;
lookerActive: BOOLFALSE;
pauseWanted: BOOLFALSE;
inputChange: CONDITION;
lookerChange: CONDITION;
wantedPriority: Process.Priority ← Process.priorityBackground;
lookerPriority: Process.Priority;
ActivateLooker: ENTRY PROC RETURNS[ BOOL ] = {
ENABLE UNWIND => NULL;
IF lookerPriority # wantedPriority
THEN Process.SetPriority[lookerPriority ← wantedPriority];
IF inputActive OR pauseWanted
THEN {
WHILE inputActive OR pauseWanted DO WAIT inputChange ENDLOOP;
RETURN[FALSE]
}
ELSE {
lookerActive ← TRUE; RETURN[TRUE]
};
};
DeactivateLooker: ENTRY PROC = {
lookerActive ← FALSE;
IF inputActive THEN NOTIFY lookerChange };
ActivateInput: ENTRY PROC = {
ENABLE UNWIND => NULL;
WHILE inputActive DO WAIT inputChange ENDLOOP;
inputActive ← TRUE;
WHILE lookerActive DO WAIT lookerChange ENDLOOP;
};
DeactivateInput: ENTRY PROC = {
inputActive ← FALSE; BROADCAST inputChange;
};
NewPriority: PUBLIC ENTRY PROC[new: Process.Priority] = {
wantedPriority ← new;
};
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: REAL;
tabTo: REAL;
tabAddr: REAL;
tabPkt: REAL;
tabData: REAL;
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;
diskDetails: CARDINAL;
InitTabs: PROC = {
tabFrom ← LookerDefs.GetLength["7777: "];
tabTo ← tabFrom +
(LookerDefs.GetLength["from"]-LookerDefs.GetLength["to"]);
tabAddr ← tabFrom + LookerDefs.GetLength["from "];
tabPkt ← tabAddr + LookerDefs.GetLength["77#777#777 "];
tabData ← tabPkt + LookerDefs.GetLength["[aData,L:777,to:177777]"];
diskFrom ← 6; -- "7777: " --
diskPkt ← diskFrom + 17; -- "from 377#377#377 " --
diskDetails ← diskPkt + 25; -- "[aData,L:1024,to:177777] " --
};
WriteChar: PROC[CHARACTER] ← LookerDefs.DisplayChar;
WriteDigit: PROC[card: CARDINAL] = INLINE {WriteChar['0+card]};
WriteMultiple: PROC[LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] ←
LookerDefs.DisplayMultiple;
WriteString: PROC[s: LONG STRING] = {
WriteMultiple[DESCRIPTOR[@(s.text),s.length]];
};
WritePup: PROC[b: Buffer] = {
WriteMultiple[DESCRIPTOR[@(b.pupChars), MIN[ContentsBytes[b],maxPupLength]]];
};
WriteOctal: PROC[n: CARDINAL] = {
SELECT n FROM
< 10B =>
Fastest case, probably most common, too.
GO TO final;
< 100B => {
Second fastest case, probably second most common, too.
WriteDigit[(n / 10B)]; GO TO final};
< 1000B => {
WriteDigit[(n / 100B)]; WriteDigit[(n / 10B) MOD 10B]; GO TO final};
>= 10000B => {
IF n >= 100000B THEN WriteChar['1];
WriteDigit[(n / 10000B) MOD 10B]};
ENDCASE;
WriteDigit[(n / 1000B) MOD 10B];
WriteDigit[(n / 100B) MOD 10B];
WriteDigit[(n / 10B) MOD 10B];
GO TO final;
EXITS final =>
WriteDigit[(n MOD 10B)];
};
WriteLongOctal: PROC[n: LONG CARDINAL] = {
SELECT n FROM
< 200000B => WriteOctal[n];
ENDCASE => {
radix: CARDINAL = 8;
radixPower: LONG CARDINAL ← 1;
lwb: LONG CARDINAL ← n/radix;
WHILE radixPower <= lwb DO radixPower ← radixPower*radix ENDLOOP;
WHILE radixPower > 0 DO
x: CARDINAL = n/radixPower;
WriteDigit[x];
n ← n - x*radixPower;
radixPower ← radixPower/radix;
ENDLOOP;
};
};
WritePair: PROC[p: PupTypes.Pair] =
{ WriteOctal[p.a]; WriteChar[',]; WriteOctal[p.b] };
WriteDecimal: PROC[n: CARDINAL] = {
tenPower: CARDINAL ← 1;
n10: CARDINAL ← n/10;
WHILE tenPower <= n10 DO tenPower ← tenPower*10 ENDLOOP;
WHILE tenPower > 0
DO x: CARDINAL = n/tenPower;
WriteDigit[x];
n ← n - x*tenPower;
tenPower ← tenPower/10;
ENDLOOP;
};
WriteAddr: PROC[addr: PupDefs.PupAddress] = INLINE {
WriteOctal[addr.net];
WriteChar['#];
WriteOctal[addr.host];
WriteChar['#];
WriteOctal[PrincOpsUtils.BITAND[addr.socket.b,777B]];
};
WriteID: PROC[n: CARDINAL] = INLINE {
WriteString["to:"];
WriteOctal[n];
};
prevMS: LONG CARDINAL ← 0;
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];
lookupFileSocket: PupTypes.PupSocketID = [a:0,b:61B];
teleSwatSocket: PupTypes.PupSocketID = TeledebugProtocol.teleSwatSocket;
Watch: PROC[ b: Buffer, time: BasicTime.Pulses ] = {
DefaultBodyPrintout: PROC[b: Buffer] = INLINE
{ WriteString["L:"]; WriteOctal[ContentsBytes[b]] };
{
mscSrv: BOOL = b.source.socket = PupTypes.miscSrvSoc
OR b.dest.socket = PupTypes.miscSrvSoc;
leaf: BOOL = b.source.socket = leafSocket
OR b.dest.socket = leafSocket;
lookupFile: BOOL = b.source.socket = lookupFileSocket
OR b.dest.socket = lookupFileSocket;
teleSwat: BOOL = b.source.socket = teleSwatSocket
OR b.dest.socket = teleSwatSocket;
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 {
WriteDecimal[(newMS-prevMS)/1000];
WriteChar['s];
}
ELSE WriteDecimal[(newMS-prevMS) MOD 10000 ];
prevMS ← newMS;
WriteChar[':];
IF mode = display
THEN {
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];
}
ELSE {
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];
};
WriteChar['[];
{
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,spare,rfa}
LOOPHOLE[140B] => "RPC-call-end",
LOOPHOLE[141B] => "RPC-data-end",
LOOPHOLE[142B] => "RPC-ack",
LOOPHOLE[143B] => "RPC-spare",
LOOPHOLE[144B] => "RPC-RFA",
LOOPHOLE[150B] => "RPC-a-call-end",
LOOPHOLE[151B] => "RPC-a-data-end",
LOOPHOLE[152B] => "RPC-ping",
LOOPHOLE[153B] => "RPC-a-spare",
LOOPHOLE[154B] => "RPC-a-RFA",
LOOPHOLE[160B] => "RPC-call-more",
LOOPHOLE[161B] => "RPC-data-more",
LOOPHOLE[170B] => "RPC-a-call-more",
LOOPHOLE[171B] => "RPC-a-data-more",
unregistered pup types (possible overlap)
gatewayRequest--200-- =>
IF lookupFile THEN "fileLookup" ELSE "gatewayRequest",
also dateTextRequest (socket 4), statisticsRequest (socket 22)
gatewayInfo--201-- =>
IF lookupFile THEN "fileInfo" ELSE "gatewayInfo",
also dateTextIs (socket 4), statisticsAre (socket 22)
dateTenexRequest--202-- =>
IF lookupFile THEN "fileError"
ELSE IF teleSwat THEN "go" ELSE "dateTenexRequest",
dateTenexIs--203-- =>
IF teleSwat THEN "goReply" ELSE "dateTenexIs",
LOOPHOLE[204B] =>
IF teleSwat THEN "ack" ELSE "pt204",
dateAltoRequest--206-- => "dateAltoRequest",
dateAltoIs--207-- => "dateAltoIs",
mailCheck--210-- => "mailCheck",
mailIsNew--211-- => "mailIsNew",
mailNotNew--212-- => "mailNotNew",
mailError--213-- => "mailError",
mailCheckLaurel--214-- => "mailCheckL",
nameLookup--220-- => "nameLookup",
nameIs--221-- => "nameIs",
nameError--222-- => "nameError",
addressLookup--223-- => "addrLookup",
addressIs--224-- => "addrIs",
whereIsUser--230-- => "whereIsUser",
userIs--231-- => "userIs",
userError--232-- => "userError",
netDirVersion--240-- => "netDirVersion",
also eventReport (socket 30)
sendNetDir--241-- => "sendNetDir",
also eventReportReply (socket 30)
bootFileSend--244-- => "bootFileSend",
kissOfDeath--247-- => "kissOfDeath",
request--250-- =>
IF mscSrv THEN "userAuthReq" ELSE "request",
result--251-- =>
IF mscSrv THEN "userAuthOk" ELSE "result",
unsolicited--252-- =>
IF mscSrv THEN "userAuthBad" ELSE "unsolicited",
custodian--253-- => "custodian",
sync--254-- => "sync",
pineAck--255-- => "pineAck",
noop--256-- => "noop",
bootDirReq--257-- => "bootDirReq",
bootDirReply--260-- =>
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?",
TeledebugProtocol.coreStoreRequest--300-- => "wCore",
TeledebugProtocol.coreFetchRequest--301-- => "rCore",
TeledebugProtocol.diskAddressSetRequest--302-- => "diskAddr",
TeledebugProtocol.diskStoreRequest--303-- => "wDisk",
TeledebugProtocol.diskFetchRequest--304-- => "rDisk",
ENDCASE => NIL;
IF type = NIL
THEN {
WriteString["pt"];
WriteOctal[LOOPHOLE[b.pupType, CARDINAL]];
}
ELSE WriteString[type];
};
WriteChar[',];
SELECT b.pupType FROM
error =>
SELECT b.errorCode FROM
badChecksumPupErrorCode =>
WriteString["badChecksum"L];
noProcessPupErrorCode =>
WriteString["noSuchPort"L];
resourceLimitsPupErrorCode =>
WriteString["resourceLimits"L];
inconsistentPupErrorCode =>
WriteString["inconsistentPup"L];
cantGetTherePupErrorCode =>
WriteString["cantGetThere"L];
hostDownPupErrorCode =>
WriteString["hostDown"L];
eightHopsPupErrorCode =>
WriteString["sixteenHops"L];
tooBigPupErrorCode =>
WriteString["tooBigPup"L];
iAmNotAGatewayPupErrorCode =>
WriteString["iAmNotAGateway"L];
gatewayResourceLimitsPupErrorCode =>
WriteString["gatewayResources"L];
ENDCASE => WriteOctal[LOOPHOLE[b.errorCode]];
mailCheck, mailCheckLaurel, nameLookup =>
IF mode # disk THEN WritePup[b];
nameIs =>
WriteAddr[b.nameIs[0]];
abort =>
WriteOctal[b.abortCode];
data, aData => {
l: CARDINAL = ContentsBytes[b];
WriteID[b.pupID.b+l];
WriteString[",L:"];
WriteOctal[l];
};
ack => {
WriteID[b.pupID.b];
WriteString[",pups:"];
WriteOctal[b.numberOfPupsAhead];
};
mark, aMark => {
WriteID[b.pupID.b+1];
WriteString[",mk:"];
WriteOctal[b.pupBytes[0]];
};
rfc, echoMe => {
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 {
IF b.dest.socket.b IN [50B..57B]
AND b.dest.socket.a IN [0..1]
THEN { -- 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];
}
ELSE IF b.dest.socket.a = 0
AND b.dest.socket.b IN [0 .. 256)
THEN WriteOctal[b.dest.socket.b];
};
};
end, endRep, int, intRep => NULL;
IN [ LOOPHOLE[140B]..LOOPHOLE[171B] ] => --Cedar RPC packet--
{
*** RPC header layout, stolen from RPCPkt.mesa --
RPCHeader: TYPE = MACHINE DEPENDENT RECORD[
length (0:0..14): [0..77777B],
oddByte (0:15..15): { no(0), yes(1) },
type (1):  RPCPktType,
destPSB (2):  CARDINAL--PSB.PsbIndex--,-- field has 6 extra bits
srcePSB (3):  CARDINAL--PSB.PsbIndex--,-- field has 6 extra bits
destHost (4):  CARDINAL--Machine--,
destSoc (5):  PupTypes.PupSocketID,
srceHost (7):  CARDINAL--Machine--,
srceSoc (8):  PupTypes.PupSocketID,
end of standard Pup header --
convLS (10):  CARDINAL--PktConversationID--,
convMS (11):  CARDINAL,
For secure conversations, the remainder of the packet must be encrypted --
pktID (12):  RPCPktID,
dispatcher (16): DispatcherDetails ];
RPCPktType: TYPE = MACHINE DEPENDENT RECORD[ -- "type" word of a Pup --
transport (0:0..7): [0..255], -- should be zero before sending --
subType (0:8..10): { rpc(3B), (7B) },
eom (0:11..11): { end(0), notEnd(1) },
ack (0:12..12): { dontAck(0), pleaseAck(1) },
class (0:13..15): { call(0), data(1), ack(2), rfa(4), (7) } ];
RPCPktID: TYPE = MACHINE DEPENDENT RECORD[
[ConversationID,PktID] uniquely identifies pkt for all hosts and time --
activity(0): CARDINAL--PSB.PsbIndex--,-- field has 6 extra bits--
callSeqLS(1): CARDINAL--CallCount--,
callSeqMS(2): CARDINAL,
pktSeq(3): CARDINAL ];
DispatcherDetails: TYPE = MACHINE DEPENDENT RECORD[
mds: CARDINAL, -- top half of dispatcher's MDS base address --
dispatcherID: LONG CARDINAL--DispatcherID--,
dispatcherHint: CARDINAL--ExportHandle-- -- hint to exporter host's export table -- ];
*** --
h: LONG POINTER TO RPCHeader = LOOPHOLE[b];
overhead: CARDINAL = SIZE[RPCHeader]+1--checksum--;
WriteString["Len:"];
IF h.length < overhead
THEN { WriteString["?-"L]; WriteOctal[overhead-h.length] }
ELSE WriteOctal[h.length-overhead];
WriteString[",Cnv:"]; WriteOctal[h.convLS MOD 1000B];
WriteString[",Call:"]; WriteOctal[h.pktID.callSeqLS MOD 1000B];
WriteString[",Pkt:"]; WriteOctal[h.pktID.pktSeq MOD 1000B];
IF h.type.class = call
THEN { WriteString[",Disp:"]; WriteOctal[h.dispatcher.dispatcherHint MOD 1000B] };
};
addressLookup =>
WriteAddr[b.nameIs[0]];
gatewayRequest =>
IF lookupFile AND mode # disk THEN WritePup[b];
bootDirReply =>
IF leaf
THEN {
id: SequinID = LOOPHOLE[b.pupID];
IF b.dest.socket = leafSocket
THEN {
WriteString["rec:"];
WriteOctal[id.receiveSequence];
WriteString[",send:"];
WriteOctal[id.sendSequence];
}
ELSE {
WriteString["send:"];
WriteOctal[id.sendSequence];
WriteString[",rec:"];
WriteOctal[id.receiveSequence];
};
WriteString[",alloc:"];
WriteOctal[id.allocate];
IF id.control = data
THEN {
l: CARDINAL = ContentsBytes[b];
WriteString[",L:"];
WriteOctal[l];
};
};
request =>
IF mscSrv THEN WriteString[@(b.pupString)] --userAuthReq--
ELSE WriteString["Pine-request"];
result =>
IF mscSrv THEN DefaultBodyPrintout[b] --userAuthOk--
ELSE WriteString["Pine-result"];
unsolicited =>
IF mscSrv THEN DefaultBodyPrintout[b] --userAuthBad--
ELSE WriteString["Pine-unsolicited"];
custodian => WriteString["Pine-unsolicited"];
sync => NULL;
pineAck => NULL;
noop => NULL;
TeledebugProtocol.coreStoreRequest, TeledebugProtocol.coreFetchRequest =>
IF teleSwat
THEN WriteLongOctal[LOOPHOLE[@b.pupBody,
LONG POINTER TO TeledebugProtocol.CoreStoreRequest].page];
TeledebugProtocol.diskAddressSetRequest, TeledebugProtocol.diskFetchRequest =>
IF teleSwat AND ContentsBytes[b] = 2*SIZE[TeledebugProtocol.DiskAddressSetRequest]
THEN WriteLongOctal[LOOPHOLE[@b.pupBody,
LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest].page];
ENDCASE => DefaultBodyPrintout[b];
WriteChar[']];
IF mode = disk
THEN {
LookerDefs.DiskPos[diskDetails];
WriteString["ID="];
WritePair[b.pupID];
WriteString[" from="];
WritePair[LOOPHOLE[b.source.socket]];
WriteString[" to="];
WritePair[LOOPHOLE[b.dest.socket]];
{
l: CARDINAL = MIN[ContentsBytes[b],maxPupLength];
WriteChar[Ascii.CR];
LookerDefs.DiskPos[diskFrom];
LookerDefs.DiskPos[diskPkt];
IF l = 0 THEN WriteString["{empty}"] ELSE WritePup[b];
WriteChar[Ascii.CR];
LookerDefs.DiskPos[diskFrom];
LookerDefs.DiskPos[diskPkt];
WriteString["Bytes="];
IF l = 0
THEN WriteString["{empty}"]
ELSE FOR i: CARDINAL IN [0..l)
DO WriteOctal[b.pupBytes[i]];
WriteChar[Ascii.SP]
ENDLOOP;
WriteChar[Ascii.CR];
LookerDefs.DiskPos[diskFrom];
LookerDefs.DiskPos[diskPkt];
WriteString["Words="];
IF l = 0
THEN WriteString["{empty}"]
ELSE FOR i: CARDINAL IN [0..l/2)
DO WriteOctal[b.pupWords[i]];
WriteChar[Ascii.SP]
ENDLOOP;
};
}
ELSE IF b.pupType = data OR b.pupType = aData
THEN { LookerDefs.SetPos[tabData]; WritePup[b] };
WriteChar[Ascii.CR];
};
};
speed: {slow, fast} ← fast;
lookerCount: CARDINAL;
StartPause: PROC = {
WriteString["Pausing ..."L]; LookerDefs.SendNow[];
LookerDefs.NotePausing[TRUE]; pauseWanted ← TRUE;
};
LookerMain: PROC = {
countLimit: CARDINAL = 40;
Process.SetPriority[lookerPriority ← wantedPriority];
DO ENABLE ABORTED => EXIT;
this: BufferIndex = GetBuffer[];
IF ActivateLooker[]
THEN {
IF lost[this]
THEN { WriteString["Lost packet(s)"L]; WriteChar[Ascii.CR] };
Watch[buffers + this * bufferSize, times[this]];
ReturnBuffer[];
IF speed = slow
AND (lookerCount ← lookerCount+1) >= (LookerDefs.ScreenLines[] * 3) / 4
THEN StartPause[];
DeactivateLooker[];
};
ENDLOOP;
};
WriteDiskLog: PROC = {
WriteChar ← LookerDefs.DiskChar;
WriteMultiple ← LookerDefs.DiskMultiple;
mode ← disk;
WriteChar[Ascii.CR];
ReplayBuffers[];
IF logged[GetBuffer[]]
THEN {
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;
}
ELSE {
WriteString["Watching host "L];
WriteOctal[wanted.net]; WriteChar['#];
WriteOctal[wanted.host]; WriteChar['#];
WriteChar[Ascii.CR];
};
THROUGH [1..fullBuffers]
DO this: BufferIndex = GetBuffer[];
IF lost[this]
THEN { WriteString["Lost packet(s)"L]; WriteChar[Ascii.CR] };
Watch[buffers + this * bufferSize, times[this]];
logged[this] ← TRUE;
ReturnBuffer[];
ENDLOOP;
WriteChar ← LookerDefs.DisplayChar;
WriteMultiple ← LookerDefs.DisplayMultiple;
mode ← display;
LookerDefs.DiskCommit[];
};
user command input --
WriteTitle: PROC = {
LookerDefs.WriteTitle[IO.PutFR[ "PUPWatch: watching host %b#%b#",
[integer[wanted.net]], [integer[wanted.host]] ] ];
};
DoAction: PUBLIC PROC[act: LookerDefs.InputAction] =
InputAction: TYPE = RECORD[SELECT act: * FROM
fast => NULL,
newHost => [name: Rope.ROPE],
pauseContinue => NULL,
quit => NULL,
replay => NULL,
slow => NULL,
writeLog => NULL,
pktSize => [big: BOOL],
ENDCASE];
{
alreadyPaused: BOOL;
ActivateInput[];
alreadyPaused ← pauseWanted;
IF pauseWanted AND act.act # pauseContinue THEN WriteChar[Ascii.CR];
lookerCount ← 0;
WITH act: act SELECT FROM
fast => {
WriteString["Fast"L];
speed ← fast; LookerDefs.NoteSlow[FALSE]; pauseWanted ← FALSE;
};
newHost => {
WriteString["Host ... "]; LookerDefs.SendNow[];
GiveEthernet[]; -- stops driver --
{
outcome: LookupOutcome;
net, host: [0..255];
[outcome, net, host] ← Lookup[act.name];
SELECT outcome FROM
ok =>
{
WriteString["ok"L]; LookerDefs.SendNow[];
wanted.net ← [net]; wanted.host ← [host];
LookerDefs.Clear[];
FlushBuffers[];
WriteTitle[];
pauseWanted ← FALSE;
};
noResponse => WriteString["no name-lookup response"L];
badName => WriteString["name not found"L];
noRoute => WriteString["no route to that host"L];
ENDCASE => ERROR;
};
TakeEthernet[]; -- starts driver --
};
pktSize =>
AllocBuffers[big: act.big];
replay => {
LookerDefs.Clear[];
WriteString["Replay (Slow)"L];
ReplayBuffers[]; speed ← slow; LookerDefs.NoteSlow[TRUE];
pauseWanted ← FALSE;
};
slow => {
WriteString["Slow"L];
speed ← slow; LookerDefs.NoteSlow[TRUE];
pauseWanted ← FALSE;
};
start => {
InitTabs[];
AllocBuffers[big: FALSE];
PupDefs.PupPackageMake[];
wanted ← PupDefs.AnyLocalPupAddress[PupTypes.fillInSocketID]; myNet ← wanted.net;
WriteTitle[];
pauseWanted ← FALSE;
lookerProcess ← FORK LookerMain[];
TakeEthernet[];
};
stop => {
Process.Abort[lookerProcess];
JOIN lookerProcess;
GiveEthernet[];
FreeBuffers[];
};
writeLog =>
IF version = boot
THEN WriteString["""Write log"" isn't implemented in this version"L]
ELSE {
WriteString["Writing log file ... "L]; LookerDefs.SendNow[];
WriteDiskLog[];
WriteString["ok"L];
};
pauseContinue =>
IF pauseWanted
THEN { WriteString[" continuing"L]; pauseWanted ← FALSE; }
ELSE StartPause[];
ENDCASE => ERROR;
IF NOT pauseWanted OR alreadyPaused THEN WriteChar[Ascii.CR];
IF NOT pauseWanted AND act.act # stop THEN LookerDefs.NotePausing[FALSE];
DeactivateInput[];
};
Address Lookup --
LookupOutcome: TYPE = { ok, badName, noResponse, noRoute };
Lookup: PROC [name: Rope.ROPE] RETURNS[outcome: LookupOutcome, net, host: [0..255]] = {
addr: PupDefs.PupAddress;
outcome ← ok;
addr ← PupDefs.GetPupAddress[, IF name.Length[] = 0 THEN "ME" ELSE name !
PupDefs.PupNameTrouble =>
{ outcome ← SELECT code FROM noRoute => noRoute, noResponse => noResponse,
ENDCASE => badName;
CONTINUE } ];
net ← addr.net; host ← addr.host;
};
Initialisation --
version: { bcd, image, boot } ← bcd;
lookerProcess: PROCESS;
Our display module hasn't been started yet --
SELECT version FROM
image => { STOP };
bcd => { NULL };
boot => { NULL };
ENDCASE => ERROR;
We're now fully running, possibly after ether-booting --
Process.EnableAborts[@bufferChange];
Process.EnableAborts[@inputChange];
Process.EnableAborts[@lookerChange];
}.