-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.
-- GateWatcherTool.mesa, AOF, 3-Nov-84 12:22:04
-- 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 3-Nov-84 12:22:09"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.