-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- GateWatcherTool.mesa, HGM, 25-Jun-85 3:41:20 -- Please don't forget to update the herald.... DIRECTORY Display USING [replaceFlags, Text, White], FormSW USING [ AllocateItemDescriptor, ClientItemsProcType, CommandItem, Display, FindItem, newLine, ProcType, StringItem], Heap USING [systemZone], Inline USING [LowHalf], MsgSW USING [Post], Process USING [Detach, Pause], Profile USING [GetUser], Runtime USING [IsBound, UnboundProcedure], String USING [AppendString, AppendChar, AppendNumber, AppendLongNumber], System USING [GetClockPulses], Time USING [AppendCurrent, Current], Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW, AddThisSW], ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType], Window USING [Box, Handle, Place], WindowFont USING [FontHeight], Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer], BootServerDefs USING [bootStatsRequest, bootStatsReply, BootStatsEntry], ForwarderDefs USING [ forwarderStatsRequest, forwarderStatsReply, ForwardStatsEntry, TransitMatrixEntry], GateControlDefs USING [ gateControlHalt, gateControlRestart, gateControlStatsSend, gateControlStatsAck, GateControlStatsEntry], NameServerDefs USING [nameStatsRequest, nameStatsReply, NameStatsEntry], Password USING [Status, ValidMemberOfGroup], PupDefs USING [ PupPackageMake, PupPackageDestroy, AppendErrorPup, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, PupSocketKick, SecondsToTocks, SetPupContentsWords, GetPupContentsBytes, AppendHostName, AppendPupAddress, GetPupAddress, PupNameTrouble], PupEchoServerDefs USING [echoStatsRequest, echoStatsReply, EchoStatsEntry], PupTimeServerFormat USING [timeStatsRequest, timeStatsReply, TimeStatsEntry], PupTypes USING [ PupAddress, fillInSocketID, fillInPupAddress, gatewaySoc, miscSrvSoc, echoSoc], PupWireFormat USING [BcplLongNumber, BcplToMesaLongNumber]; GateWatcherTool: MONITOR IMPORTS Display, FormSW, Heap, Inline, MsgSW, Process, Profile, Runtime, String, System, Time, Tool, ToolWindow, WindowFont, Buffer, Password, PupWireFormat, PupDefs = BEGIN OPEN PupDefs, PupTypes; msg, form, info: Window.Handle ← NIL; z: UNCOUNTED ZONE = Heap.systemZone; -- Be sure to initialize it when it is allocated!!!! data: LONG POINTER TO Data ← NIL; pool: Buffer.AccessHandle; maxLines: CARDINAL = 35; Data: TYPE = RECORD [ pleaseStop: BOOLEAN ← FALSE, alighTheSillyFink: WORD ← 0, running: BOOLEAN ← FALSE, him: PupAddress ← PupTypes.fillInPupAddress, target: LONG STRING ← NIL, fastBoot, slowBoot, newBoot, echo, name, dirsSent, route, time: LONG CARDINAL ← 0, bootSoc, echoSoc, gcSoc, nameSoc, routeSoc, timeSoc: PupSocket ← NIL, activeProcesses: CARDINAL ← 0, height: CARDINAL ← 20, line: CARDINAL ← 0, lines: ARRAY [0..maxLines) OF LONG STRING ← ALL[NIL]]; Init: PROCEDURE = BEGIN herald: STRING = "Gate Watcher of 25-Jun-85 3:41:15"L; [] ← Tool.Create[ name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition]; END; GateWatcherOn: PROCEDURE = BEGIN IF data.running THEN RETURN; ZapCounters[]; ClearThings[]; data.pleaseStop ← FALSE; IF ~FindPath[] THEN RETURN; data.activeProcesses ← 6; pool ← Buffer.MakePool[send: 4, receive: 4]; Process.Detach[FORK Boot[]]; Process.Detach[FORK Echo[]]; Process.Detach[FORK Name[]]; Process.Detach[FORK Route[]]; Process.Detach[FORK Timer[]]; Process.Detach[FORK GateControl[]]; data.running ← TRUE; UpdatePicture[]; END; GateWatcherOff: PROCEDURE = BEGIN IF data = NIL THEN RETURN; IF ~data.running THEN RETURN; WaitUntilStopped[]; data.running ← FALSE; UpdatePicture[]; END; UpdatePicture: PROCEDURE = BEGIN IF form = NIL THEN RETURN; FormSW.FindItem[form, startIX].flags.invisible ← data.running; FormSW.FindItem[form, stopIX].flags.invisible ← ~data.running; IF Runtime.IsBound[LOOPHOLE[Password.ValidMemberOfGroup]] THEN FormSW.FindItem[form, enableIX].flags.invisible ← ~data.running; FormSW.Display[form]; END; WaitUntilStopped: PROCEDURE = BEGIN IF data = NIL THEN RETURN; data.pleaseStop ← TRUE; PupSocketKick[data.bootSoc]; PupSocketKick[data.echoSoc]; PupSocketKick[data.gcSoc]; PupSocketKick[data.nameSoc]; PupSocketKick[data.routeSoc]; PupSocketKick[data.timeSoc]; WHILE data.activeProcesses # 0 DO Process.Pause[1]; ENDLOOP; Buffer.DestroyPool[pool]; END; Boot: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; where.socket ← PupTypes.miscSrvSoc; bootSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← BootServerDefs.bootStatsRequest; SetPupContentsWords[b, 0]; bootSoc.put[b]; UNTIL (b ← bootSoc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = BootServerDefs.bootStatsReply) AND (b.pup.pupID.a = packetNumber AND b.pup.pupID.b = packetNumber) => BEGIN bse: LONG POINTER TO BootServerDefs.BootStatsEntry = LOOPHOLE[@b.pup.pupBody]; data.fastBoot ← PupWireFormat.BcplToMesaLongNumber[bse.fastSends]; data.slowBoot ← PupWireFormat.BcplToMesaLongNumber[bse.slowSends]; data.newBoot ← PupWireFormat.BcplToMesaLongNumber[bse.filesRecv]; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[bootSoc]; data.activeProcesses ← data.activeProcesses - 1; END; Echo: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; where.socket ← PupTypes.echoSoc; echoSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← PupEchoServerDefs.echoStatsRequest; SetPupContentsWords[b, 0]; echoSoc.put[b]; UNTIL (b ← echoSoc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = PupEchoServerDefs.echoStatsReply) AND (b.pup.pupID.a = packetNumber AND b.pup.pupID.b = packetNumber) => BEGIN ese: LONG POINTER TO PupEchoServerDefs.EchoStatsEntry = LOOPHOLE[@b.pup.pupBody]; data.echo ← PupWireFormat.BcplToMesaLongNumber[ese.pupsEchoed]; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[echoSoc]; data.activeProcesses ← data.activeProcesses - 1; END; Name: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; where.socket ← PupTypes.miscSrvSoc; nameSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← NameServerDefs.nameStatsRequest; SetPupContentsWords[b, 0]; nameSoc.put[b]; UNTIL (b ← nameSoc.get[]) = NIL DO SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = NameServerDefs.nameStatsReply) AND (b.pup.pupID.a = packetNumber AND b.pup.pupID.b = packetNumber) => BEGIN nse: LONG POINTER TO NameServerDefs.NameStatsEntry = LOOPHOLE[@b.pup.pupBody]; data.name ← PupWireFormat.BcplToMesaLongNumber[nse.nameRequests]; data.dirsSent ← PupWireFormat.BcplToMesaLongNumber[ nse.directoriesSend]; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[nameSoc]; data.activeProcesses ← data.activeProcesses - 1; END; Timer: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; where.socket ← PupTypes.miscSrvSoc; timeSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← PupTimeServerFormat.timeStatsRequest; SetPupContentsWords[b, 0]; timeSoc.put[b]; UNTIL (b ← timeSoc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = PupTimeServerFormat.timeStatsReply) AND (b.pup.pupID.a = packetNumber AND b.pup.pupID.b = packetNumber) => BEGIN tse: LONG POINTER TO PupTimeServerFormat.TimeStatsEntry = LOOPHOLE[@b.pup.pupBody]; data.time ← PupWireFormat.BcplToMesaLongNumber[tse.tenexRequests] + PupWireFormat.BcplToMesaLongNumber[tse.stringRequests] + PupWireFormat.BcplToMesaLongNumber[tse.altoRequests]; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[timeSoc]; data.activeProcesses ← data.activeProcesses - 1; END; Route: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; where.socket ← PupTypes.gatewaySoc; routeSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[5]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← ForwarderDefs.forwarderStatsRequest; SetPupContentsWords[b, 0]; routeSoc.put[b]; UNTIL (b ← routeSoc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = ForwarderDefs.forwarderStatsReply) AND (b.pup.pupID.a = packetNumber AND b.pup.pupID.b = packetNumber) => BEGIN OPEN ForwarderDefs; fse: LONG POINTER TO ForwardStatsEntry = LOOPHOLE[@b.pup.pupBody]; l: CARDINAL ← PupDefs.GetPupContentsBytes[b]/2; n: CARDINAL ← MIN[fse.numberOfNetworks, l - SIZE[ForwardStatsEntry]]; p: CARDINAL ← (l - n - SIZE[ForwardStatsEntry])/SIZE[TransitMatrixEntry]; data.route ← PupWireFormat.BcplToMesaLongNumber[ fse.routingInfoRequests]; PrintForwardingStats[ nets: DESCRIPTOR[@b.pup.pupBody + SIZE[ForwardStatsEntry], n], pupStatsTable: DESCRIPTOR[ @b.pup.pupBody + SIZE[ForwardStatsEntry] + n, p]]; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[routeSoc]; data.activeProcesses ← data.activeProcesses - 1; END; PrintForwardingStats: ENTRY PROCEDURE [ nets: LONG DESCRIPTOR FOR ARRAY OF CARDINAL, pupStatsTable: LONG DESCRIPTOR FOR ARRAY OF ForwarderDefs.TransitMatrixEntry] = BEGIN FindFirst: INTERNAL PROCEDURE [from, to: CARDINAL] = BEGIN tme: LONG POINTER TO ForwarderDefs.TransitMatrixEntry; from ← from MOD 400B; to ← to MOD 400B; FOR i: CARDINAL IN [0..LENGTH[pupStatsTable]) DO tme ← @pupStatsTable[i]; IF from # tme.sourceNet OR to # tme.destNet THEN LOOP; LD10Dash[PupWireFormat.BcplToMesaLongNumber[tme.count]]; EXIT; REPEAT FINISHED => LD10Dash[0]; ENDLOOP; END; FindSecond: INTERNAL PROCEDURE [from, to: CARDINAL] = BEGIN i: CARDINAL; tme: LONG POINTER TO ForwarderDefs.TransitMatrixEntry; from ← from MOD 400B; to ← to MOD 400B; FOR i ← 0, i + 1 UNTIL i = LENGTH[pupStatsTable] DO tme ← @pupStatsTable[i]; IF from # tme.sourceNet OR to # tme.destNet THEN LOOP; EXIT; -- Skip first matching entry REPEAT FINISHED => BEGIN LD10Dash[0]; RETURN; END; ENDLOOP; FOR i ← i + 1, i + 1 UNTIL i = LENGTH[pupStatsTable] DO tme ← @pupStatsTable[i]; IF from # tme.sourceNet OR to # tme.destNet THEN LOOP; LD10Dash[PupWireFormat.BcplToMesaLongNumber[tme.count]/1000]; EXIT; REPEAT FINISHED => LD10Dash[0]; ENDLOOP; END; SetLinePointer[6]; WriteLine["Packets forwarded:"L]; WriteLine["from to"L]; WriteString[" Discard"L]; FOR to: CARDINAL IN [0..LENGTH[nets]) DO WriteString[" "L]; O4[nets[to]]; ENDLOOP; WriteCR[]; FOR from: CARDINAL IN [0..LENGTH[nets]) DO O4[nets[from]]; FindFirst[nets[from], 0]; FOR to: CARDINAL IN [0..LENGTH[nets]) DO FindFirst[nets[from], nets[to]]; ENDLOOP; WriteCR[]; ENDLOOP; WriteCR[]; WriteLine["KBytes forwarded:"L]; WriteLine["from to"L]; WriteString[" Discard"L]; FOR to: CARDINAL IN [0..LENGTH[nets]) DO WriteString[" "L]; O4[nets[to]]; ENDLOOP; WriteCR[]; FOR from: CARDINAL IN [0..LENGTH[nets]) DO O4[nets[from]]; FindSecond[nets[from], 0]; FOR to: CARDINAL IN [0..LENGTH[nets]) DO FindSecond[nets[from], nets[to]]; ENDLOOP; WriteCR[]; ENDLOOP; END; GateControl: PROCEDURE = BEGIN OPEN data; b: PupBuffer; packetNumber: CARDINAL ← LAST[CARDINAL]; where: PupAddress ← him; text: STRING = [50]; String.AppendString[text, target]; String.AppendString[text, " ["L]; AppendPupAddress[text, him]; String.AppendString[text, "]"L]; where.socket ← [31415, 9265]; gcSoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; UNTIL pleaseStop DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← 27182; b.pup.pupID.b ← (packetNumber ← packetNumber + 1); b.pup.pupType ← GateControlDefs.gateControlStatsSend; SetPupContentsWords[b, 0]; gcSoc.put[b]; UNTIL (b ← gcSoc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = GateControlDefs.gateControlStatsAck) AND (b.pup.pupID.b = packetNumber) => PrintFirstHalf[text, b]; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ENDLOOP; PupSocketDestroy[gcSoc]; data.activeProcesses ← data.activeProcesses - 1; END; PrintFirstHalf: ENTRY PROCEDURE [text: LONG STRING, b: PupBuffer] = BEGIN gse: LONG POINTER TO GateControlDefs.GateControlStatsEntry; gse ← LOOPHOLE[@b.pup.pupBody]; SetLinePointer[0]; WriteString[text]; PrintUpTime[gse.startTime]; WriteCR[]; FOR i: CARDINAL IN [0..gse.versionText.length) DO WriteChar[gse.versionText.char[i]]; ENDLOOP; WriteCR[]; WriteCR[]; PrintItem[data.fastBoot, "Boot: "L]; PrintItem[data.echo, "Echo: "L]; PrintItem[data.name, "Name: "L]; PrintItem[data.route, "Route: "L]; PrintItem[data.time, "Time: "L]; WriteCR[]; IF (data.slowBoot + data.newBoot + data.dirsSent) # 0 THEN BEGIN PrintItem[data.slowBoot, "SlowBoot: "L]; PrintItem[data.newBoot, "NewBoot: "L]; PrintItem[data.dirsSent, "NetDirsSent: "L]; WriteCR[]; END; PrintItem[gse.freeBuffers, "Buffers: "L]; PrintItem[gse.freeDiskPages, "Disk pages: "L]; WriteCR[]; END; PrintUpTime: INTERNAL PROCEDURE [startTime: PupWireFormat.BcplLongNumber] = BEGIN now, then: LONG INTEGER; sec: LONG INTEGER; min: LONG INTEGER; hours: LONG INTEGER; WriteString[" up "L]; then ← PupWireFormat.BcplToMesaLongNumber[startTime]; now ← Time.Current[]; sec ← now - then; hours ← sec/3600; sec ← sec - hours*3600; min ← sec/60; sec ← sec - min*60; WriteLongDecimal[hours]; WriteChar[':]; IF min < 10 THEN WriteChar['0]; WriteLongDecimal[min]; WriteChar[':]; IF sec < 10 THEN WriteChar['0]; WriteLongDecimal[sec]; END; PrintItem: INTERNAL PROCEDURE [d: LONG CARDINAL, s: LONG STRING] = BEGIN IF d = 0 THEN RETURN; WriteString[s]; WriteLongDecimal[d]; WriteString[" "L]; END; FindPath: PROCEDURE RETURNS [BOOLEAN] = BEGIN text: STRING = [100]; String.AppendString[text, "Looking at "L]; String.AppendString[text, data.target]; String.AppendString[text, "="L]; GetPupAddress[ @data.him, data.target ! PupNameTrouble => BEGIN String.AppendString[text, e]; MsgSW.Post[msg, text]; GOTO Trouble; END]; AppendPupAddress[text, data.him]; MsgSW.Post[msg, text]; RETURN[TRUE]; EXITS Trouble => RETURN[FALSE]; END; CheckPassword: PROCEDURE RETURNS [ok: BOOLEAN] = BEGIN person: STRING = [100]; pwd: STRING = [100]; machine: STRING ← [100]; status: Password.Status; SaveUserInfo: PROCEDURE [name, password: LONG STRING] = BEGIN String.AppendString[person, name]; String.AppendString[pwd, password]; END; ok ← TRUE; IF ~data.running THEN BEGIN MsgSW.Post[msg, "Not looking at a target yet"L]; RETURN[FALSE]; END; Profile.GetUser[SaveUserInfo, registry]; AppendHostName[machine, data.him]; -- target might have been edited String.AppendString[machine, ".internet"L]; status ← Password.ValidMemberOfGroup[person, pwd, machine ! Runtime.UnboundProcedure => BEGIN MsgSW.Post[msg, "Password checker and/or GrapevineUser not loaded"L]; ok ← FALSE; CONTINUE; END]; IF ~ok THEN RETURN; SELECT status FROM yes => RETURN[TRUE]; nil => MsgSW.Post[msg, "Confusion about NIL"L]; allDown => MsgSW.Post[msg, "All Grapevine servers appear to be down"L]; notFound => MsgSW.Post[msg, "Grapevine doesn't like your name"L]; badPwd => MsgSW.Post[msg, "Grapevine doesn't like your password"L]; group => MsgSW.Post[msg, "Grapevine thinks you are a group"L]; no => BEGIN temp: STRING = [100]; String.AppendString[temp, "You are not in the appropiate group ("L]; String.AppendString[temp, machine]; String.AppendString[temp, ")"L]; MsgSW.Post[msg, temp]; END; notGroup => BEGIN temp: STRING = [100]; String.AppendString[temp, "Grapevine doesn't recognize this machine's group ("L]; String.AppendString[temp, machine]; String.AppendString[temp, ")"L]; MsgSW.Post[msg, temp]; END; error => MsgSW.Post[msg, "Error from GrapevineUser package"L]; ENDCASE => ERROR; RETURN[FALSE]; END; -- IO things SetLinePointer: INTERNAL PROCEDURE [n: [0..maxLines)] = INLINE BEGIN data.line ← n; data.lines[data.line].length ← 0; END; WriteChar: INTERNAL PROCEDURE [c: CHARACTER] = BEGIN String.AppendChar[data.lines[data.line], c]; END; WriteCR: INTERNAL PROCEDURE = BEGIN string: LONG STRING = data.lines[data.line]; tail: Window.Place; tail ← Display.Text[ window: info, string: string, place: [indentation, data.line*data.height], flags: Display.replaceFlags ]; Display.White[info, [tail, [rightEdge - tail.x, data.height]]]; data.line ← data.line + 1; IF data.line = maxLines THEN data.line ← maxLines - 1; data.lines[data.line].length ← 0; END; WriteString: INTERNAL PROCEDURE [s: LONG STRING] = BEGIN i: CARDINAL; FOR i IN [0..s.length) DO WriteChar[s[i]]; ENDLOOP; END; WriteLine: INTERNAL PROCEDURE [s: LONG STRING] = BEGIN WriteString[s]; WriteCR[]; END; WriteLongDecimal: INTERNAL PROCEDURE [n: LONG CARDINAL] = BEGIN s: STRING = [20]; String.AppendLongNumber[s, n, 10]; WriteString[s]; END; LD10: INTERNAL PROCEDURE [n: LONG INTEGER] = BEGIN s: STRING = [20]; String.AppendLongNumber[s, n, 10]; THROUGH [s.length..10) DO WriteChar[' ]; ENDLOOP; WriteString[s]; END; LD10Dash: INTERNAL PROCEDURE [n: LONG INTEGER] = BEGIN IF n = 0 THEN WriteString[" - "L] ELSE LD10[n]; END; WriteDecimal: INTERNAL PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END; WriteOctal: INTERNAL PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END; WriteNumber: INTERNAL PROCEDURE [n, radix, width: CARDINAL] = INLINE BEGIN temp: STRING = [25]; String.AppendNumber[temp, n, radix]; THROUGH [temp.length..width) DO WriteChar[' ]; ENDLOOP; WriteString[temp]; END; D8: INTERNAL PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 8]; END; O3: INTERNAL PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O4: INTERNAL PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 4]; END; O6: INTERNAL PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O9: INTERNAL PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END; WriteCurrentDateAndTime: INTERNAL PROCEDURE = BEGIN text: STRING = [20]; Time.AppendCurrent[text]; WriteString[text]; END; ShowErrorPup: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] = BEGIN text: STRING = [100]; PupDefs.AppendErrorPup[text, b]; MsgSW.Post[msg, text]; END; ClearThings: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..maxLines) DO data.lines[i].length ← 0; ENDLOOP; RepaintThings[]; END; indentation: CARDINAL = 10; rightEdge: CARDINAL = 600; RepaintThings: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..maxLines) DO tail: Window.Place; tail ← Display.Text[ window: info, string: data.lines[i], place: [indentation, i*data.height], flags: Display.replaceFlags ]; Display.White[info, [tail, [rightEdge - tail.x, data.height]]]; ENDLOOP; END; DisplayInfo: ToolWindow.DisplayProcType = BEGIN RepaintThings[]; END; ZapCounters: PROCEDURE = BEGIN data.fastBoot ← data.slowBoot ← data.newBoot ← data.echo ← data.name ← data.dirsSent ← data.route ← data.time ← 0; END; HaltRestart: PROCEDURE [what: {halt, restart}] = BEGIN soc: PupSocket; sequenceNumber: CARDINAL ← Inline.LowHalf[System.GetClockPulses[]]; where: PupAddress ← data.him; where.socket ← [31415, 9265]; soc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[10]]; THROUGH [0..10) DO b: PupBuffer ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupID.a ← 27182; b.pup.pupID.b ← sequenceNumber; b.pup.pupType ← SELECT what FROM halt => GateControlDefs.gateControlHalt, restart => GateControlDefs.gateControlRestart, ENDCASE => ERROR; SetPupContentsWords[b, 0]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => ShowErrorPup[b]; (b.pup.pupType = GateControlDefs.gateControlStatsAck) AND (b.pup.pupID.b = sequenceNumber) => BEGIN MsgSW.Post[msg, "Ok"L]; GOTO Hit; END; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; LOOP; END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b]; REPEAT Hit => NULL; FINISHED => MsgSW.Post[msg, "No response"L]; ENDLOOP; PupSocketDestroy[soc]; END; Start: FormSW.ProcType = BEGIN GateWatcherOn[]; END; Stop: FormSW.ProcType = BEGIN GateWatcherOff[]; Disable[]; END; Enable: FormSW.ProcType = BEGIN IF ~CheckPassword[] THEN RETURN; FormSW.FindItem[form, haltIX].flags.invisible ← FALSE; FormSW.FindItem[form, restartIX].flags.invisible ← FALSE; FormSW.Display[form]; END; Disable: PROCEDURE = BEGIN FormSW.FindItem[form, haltIX].flags.invisible ← TRUE; FormSW.FindItem[form, restartIX].flags.invisible ← TRUE; FormSW.Display[form]; END; Halt: FormSW.ProcType = BEGIN HaltRestart[halt]; END; Restart: FormSW.ProcType = BEGIN HaltRestart[restart]; END; MakeSWs: Tool.MakeSWsProc = BEGIN msg ← Tool.MakeMsgSW[window: window, lines: 5]; form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; MakeInfoSW[window]; END; MakeInfoSW: PROCEDURE [window: Window.Handle] = BEGIN box: Window.Box ← ToolWindow.nullBox; box.dims.h ← 20*36; info ← ToolWindow.CreateSubwindow[parent: window, display: DisplayInfo, box: box]; Tool.AddThisSW[window: window, sw: info, swType: vanilla]; END; startIX: CARDINAL = 0; stopIX: CARDINAL = 1; enableIX: CARDINAL = 2; haltIX: CARDINAL = 3; restartIX: CARDINAL = 4; MakeForm: FormSW.ClientItemsProcType = BEGIN nParams: CARDINAL = 6; items ← FormSW.AllocateItemDescriptor[nParams]; items[startIX] ← FormSW.CommandItem[ tag: "Start"L, proc: Start, place: FormSW.newLine]; items[stopIX] ← FormSW.CommandItem[ tag: "Stop"L, proc: Stop, place: FormSW.newLine, invisible: TRUE]; items[enableIX] ← FormSW.CommandItem[tag: "Enable"L, proc: Enable, invisible: TRUE]; items[haltIX] ← FormSW.CommandItem[tag: "Halt"L, proc: Halt, invisible: TRUE]; items[restartIX] ← FormSW.CommandItem[tag: "Restart"L, proc: Restart, invisible: TRUE]; items[5] ← FormSW.StringItem[tag: "Target"L, string: @data.target, inHeap: TRUE]; RETURN[items, TRUE]; END; AlreadyActive: ERROR = CODE; NotActive: ERROR = CODE; ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM old = inactive => BEGIN IF data # NIL THEN ERROR AlreadyActive; data ← z.NEW[Data]; data↑ ← []; data.height ← WindowFont.FontHeight[]; data.target ← z.NEW[StringBody[20]]; String.AppendString[data.target, "ME"L]; FOR i: CARDINAL IN [0..maxLines) DO data.lines[i] ← z.NEW[StringBody[150]]; ENDLOOP; [] ← PupDefs.PupPackageMake[]; END; new = inactive => BEGIN IF data = NIL THEN ERROR NotActive; msg ← form ← info ← NIL; IF data.running THEN GateWatcherOff[]; PupDefs.PupPackageDestroy[]; FOR i: CARDINAL IN [0..maxLines) DO z.FREE[@data.lines[i]]; ENDLOOP; z.FREE[@data.target]; z.FREE[@data]; END; ENDCASE; END; -- Main Body Init[]; END.