<> <> <> <> <> <<>> <> DIRECTORY Ascii USING [CR, SP], Basics USING [BITOR, BITSHIFT, bitsPerWord, bytesPerWord], BasicTime USING [GetClockPulses, Now, Pulses, PulsesToMicroseconds], Buttons USING [Button, ButtonProc, Create, ReLabel, SetDisplayStyle], CommBuffer USING [], CommDriver USING [Buffer, CreateInterceptor, DestroyInterceptor, GetNetworkChain, Interceptor, Network, RecvInterceptor, RecvType], CommDriverType USING [Encapsulation], Containers USING [ChildXBound, ChildYBound, Create], Convert USING [CardFromRope, Error, RopeFromCard], FS USING [Copy, Error, StreamOpen], Imager USING [black, Box, Context, MaskBox, Rectangle, SetColor, SetFont, SetXY, ShowText, white], ImagerBackdoor USING [GetBounds, GetCP], ImagerFont USING [Font, FontBoundingBox, RopeWidth], IO USING [Close, Put, PutChar, PutRope, STREAM], Labels USING [Create], PrincOpsUtils USING [LongCopy], Process USING [Abort, Detach, EnableAborts, Priority, priorityBackground, priorityNormal, SetPriority], Pup USING [allHosts], PupBuffer USING [Buffer], RefText USING [AppendChar, AppendRope], Rope USING [Cat, Length, ROPE, Text], VFonts USING [EstablishFont], ViewerClasses USING [DestroyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec], ViewerOps USING [CreateViewer, FindViewer, OpenIcon, PaintViewer, RegisterViewerClass, RestoreViewer], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection], VM USING [AddressForPageNumber, Allocate, Free, Interval, PagesForWords], XNS USING [broadcastHost, Host]; EtherWatch: MONITOR IMPORTS Basics, BasicTime, Buttons, CommDriver, Containers, Convert, FS, Imager, ImagerBackdoor, ImagerFont, IO, Labels, PrincOpsUtils, Process, RefText, Rope, VFonts, ViewerOps, ViewerTools, VM EXPORTS CommBuffer = { <> <<>> BYTE: TYPE = [0..100H); Encapsulation: PUBLIC TYPE = CommDriverType.Encapsulation; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; <<>> MSec: PROC [rawClock: BasicTime.Pulses] RETURNS [LONG CARDINAL] = INLINE { RETURN[BasicTime.PulsesToMicroseconds[rawClock]/1000]; }; <> InputAction: TYPE = RECORD [SELECT act: * FROM fast => NULL, newHost => [who: ROPE], pauseContinue => NULL, replay => NULL, slow => NULL, start => NULL, stop => NULL, writeLog => NULL, pktSize => [big: BOOL], ENDCASE]; <> bufferSize: INT; copySize: NAT; maxLength: NAT; bigLength: NAT = 3000; smallLength: NAT = 100; Buffer: TYPE = LONG POINTER TO BufferData; BufferData: TYPE = RECORD [ type: CommDriver.RecvType, time: BasicTime.Pulses, length: NAT, encap: Encapsulation, body: SELECT OVERLAID * FROM null => [], bytes => [bytes: PACKED ARRAY [0..4096) OF BYTE], small => [PACKED ARRAY [0..smallLength) OF BYTE], big => [PACKED ARRAY [0..bigLength) OF BYTE], ENDCASE ]; nBuffers: CARDINAL = 1000; BufferIndex: TYPE = [0..nBuffers); bufferSpace: VM.Interval; buffers: LONG POINTER TO --ARRAY BufferIndex OF-- BufferData _ NIL; lost: PACKED ARRAY BufferIndex OF BOOL _ ALL[FALSE]; logged: PACKED ARRAY BufferIndex OF BOOL _ 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; lookerWaiting: BOOL _ FALSE; allUsed: BOOL _ FALSE; -- whether all buffers have been written lookerProcess: PROCESS; AllocBuffers: ENTRY PROC [big: BOOL] = { IF buffers # NIL THEN FreeBuffers[]; bufferSize _ IF big THEN SIZE[big BufferData] ELSE SIZE[small BufferData]; copySize _ CARDINAL[bufferSize - SIZE[null BufferData]]; bufferSpace _ VM.Allocate[VM.PagesForWords[nBuffers * bufferSize]]; buffers _ VM.AddressForPageNumber[bufferSpace.page]; maxLength _ IF big THEN bigLength ELSE smallLength; InnerFlush[]; }; FreeBuffers: PROC = { VM.Free[bufferSpace]; }; 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: BOOL _ FALSE; 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: CARDINAL _ 0; -- 3MB big: REF BOOL _ NEW[BOOL _ FALSE]; background: REF BOOL _ NEW[BOOL _ TRUE]; broadcast: REF BOOL _ NEW[BOOL _ FALSE]; pup: REF BOOL _ NEW[BOOL _ TRUE]; rpc: REF BOOL _ NEW[BOOL _ TRUE]; xns: REF BOOL _ NEW[BOOL _ TRUE]; arpa: REF BOOL _ NEW[BOOL _ TRUE]; breathOfLife: REF BOOL _ NEW[BOOL _ TRUE]; other: REF BOOL _ NEW[BOOL _ TRUE]; error: REF BOOL _ NEW[BOOL _ TRUE]; EthernetOneHostFilter: PROC [buffer: CommDriver.Buffer] RETURNS [reject: BOOL] = { IF wanted = 0 THEN RETURN [FALSE]; IF broadcast^ AND buffer.ovh.encap.ethernetOneDest = Pup.allHosts THEN RETURN [FALSE]; IF buffer.ovh.encap.ethernetOneDest = wanted THEN RETURN [FALSE]; IF buffer.ovh.encap.ethernetOneSource = wanted THEN RETURN [FALSE]; RETURN[TRUE]; }; Recv: ENTRY CommDriver.RecvInterceptor = TRUSTED { words: NAT = (bytes + Basics.bytesPerWord - 1) / Basics.bytesPerWord; myBuffer: BufferIndex = wBuffer; copy: Buffer _ buffers + myBuffer * bufferSize; SELECT recv FROM arpa => IF ~arpa^ THEN RETURN; xns, xnsTranslate => IF ~xns^ THEN RETURN; pup, pupTranslate => { b: PupBuffer.Buffer = LOOPHOLE[buffer]; SELECT b.type.ORD FROM IN [140B..177B] => IF ~rpc^ THEN RETURN; ENDCASE => IF ~pup^ THEN RETURN; }; other => { SELECT TRUE FROM network.type = ethernetOne => -- 3MB SELECT buffer.ovh.encap.ethernetOneType FROM breathOfLife => IF ~breathOfLife^ THEN RETURN; ENDCASE => IF ~other^ THEN RETURN; ENDCASE => IF ~other^ THEN RETURN; }; error => IF ~error^ THEN RETURN; ENDCASE => NULL; -- I wish the Compiler would catch these. IF EthernetOneHostFilter[buffer] THEN RETURN; -- 3MB copy.type _ recv; copy.time _ BasicTime.GetClockPulses[]; copy.length _ bytes; copy.encap _ buffer.ovh.encap; PrincOpsUtils.LongCopy[ from: @buffer.data, to: @copy.body, nwords: MIN[words, copySize] ]; NextBuffer[]; }; interceptor: CommDriver.Interceptor; network: CommDriver.Network _ CommDriver.GetNetworkChain[]; TakeEthernet: ENTRY PROC = { recvMask: PACKED ARRAY CommDriver.RecvType OF BOOL _ ALL[TRUE]; interceptor _ CommDriver.CreateInterceptor[ network: network, sendMask: ALL[FALSE], sendProc: NIL, recvMask: recvMask, recvProc: Recv, data: NIL, promiscuous: TRUE]; }; GiveEthernet: ENTRY PROC = { CommDriver.DestroyInterceptor[interceptor]; }; <> inputActive: BOOL _ FALSE; lookerActive: BOOL _ FALSE; pauseWanted: BOOL _ FALSE; 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; }; <> mode: {display, disk} _ display; dataBytesPerLine: NAT = 30; tenMB: BOOL _ FALSE; <> <