-- Copyright (C) 1983 by Xerox Corporation. All rights reserved.
-- FindBootFiles.mesa, HGM, 24-Sep-83 13:37:27
DIRECTORY
Display USING [Bitmap, Invert, replaceFlags, White],
Format USING [], -- Needed by Put.Number and Put.Date
FormSW USING [
ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem,
StringItem, NumberItem],
Heap USING [Create, Delete, MakeNode],
MsgSW USING [Post],
Process USING [Detach, Yield],
Put USING [Char, CR, Date, Decimal, Text, Line, Number, LongDecimal],
Runtime USING [GetBcdTime],
String USING [AppendString, AppendNumber, CopyToNewString, Equivalent],
System USING [GreenwichMeanTime],
Time USING [Append, AppendCurrent, Unpack],
Tool USING [
Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW,
AddThisSW],
ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType],
Window USING [Handle, Box],
Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
PupWireFormat USING [BcplLongNumber, BcplToMesaLongNumber, BcplSTRING],
PupDefs USING [
PupPackageMake, PupPackageDestroy,
PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake,
defaultNumberOfNetworks, GetHopsToNetwork, SecondsToTocks,
SetPupContentsWords, GetPupContentsBytes, AppendPupAddress, AppendHostName,
AppendErrorPup, GetPupAddress, PupNameTrouble],
PupTypes USING [PupAddress, fillInSocketID, miscSrvSoc];
FindBootFiles: PROGRAM
IMPORTS
Display, FormSW, Heap, MsgSW, Process, Put, Runtime, String,
Time, Tool, ToolWindow,
Buffer, PupDefs, PupWireFormat =
BEGIN OPEN PupDefs, PupTypes;
msg, form, boxes, log: Window.Handle;
defaultMaxHops: CARDINAL = 3;
pleaseStop: BOOLEAN ← FALSE;
running: BOOLEAN ← FALSE;
indicator: {left, right, off} ← off;
first: BootFile ← NIL;
maxHops: CARDINAL ← defaultMaxHops;
where: PupAddress ← [[0], [0], PupTypes.miscSrvSoc];
target: LONG STRING ← NIL;
BootFile: TYPE = LONG POINTER TO BootFileInfo;
BootFileInfo: TYPE = RECORD [
next: BootFile,
number: WORD,
where: PupTypes.PupAddress,
date: System.GreenwichMeanTime,
name: LONG STRING];
ScanCircle: FormSW.ProcType =
BEGIN
IF running THEN
BEGIN MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; END;
running ← TRUE;
Process.Detach[FORK DoCircle[]];
END;
ScanTarget: FormSW.ProcType =
BEGIN
IF running THEN
BEGIN MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; END;
WriteCR[];
WriteCurrentDateAndTime[];
WriteString[" Finding boot files on "L];
IF ~FindPath[] THEN RETURN;
running ← TRUE;
Process.Detach[FORK DoOne[]];
END;
Stop: FormSW.ProcType = BEGIN Off[]; END;
Off: PROCEDURE =
BEGIN
IF ~running THEN RETURN;
pleaseStop ← TRUE;
WHILE running DO Process.Yield[]; ENDLOOP;
pleaseStop ← FALSE;
END;
FindPath: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
WriteString[target];
WriteChar['=];
GetPupAddress[
@where, target !
PupNameTrouble =>
BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END];
PrintPupAddress[where];
WriteLine["."L];
RETURN[TRUE];
EXITS Trouble => RETURN[FALSE];
END;
DoCircle: PROCEDURE =
BEGIN
first: BOOLEAN ← TRUE;
SetupBoxes[];
FOR net: CARDINAL IN [1..PupDefs.defaultNumberOfNetworks) DO
IF GetHopsToNetwork[[net]] > maxHops THEN LOOP;
where ← [[net], [0], miscSrvSoc];
IF first THEN Put.Text[log, "Searching network "] ELSE Put.Text[log, ", "];
Put.Number[log, net, [8, FALSE, TRUE, 0]];
Put.Char[log, '(];
Put.Decimal[log, GetHopsToNetwork[[net]]];
Put.Char[log, ')];
SearchOne[];
first ← FALSE;
ENDLOOP;
Put.CR[log];
SetDownBoxes[];
PrintBootFileList[];
ForgetBootFileList[];
running ← FALSE;
END;
DoOne: PROCEDURE =
BEGIN
SetupBoxes[];
SearchOne[];
SetDownBoxes[];
PrintBootFileList[];
ForgetBootFileList[];
running ← FALSE;
END;
ForgetBootFileList: PROCEDURE =
BEGIN
ZapHeap[];
first ← NIL;
END;
SearchOne: PROCEDURE =
BEGIN
pool: Buffer.AccessHandle ← Buffer.MakePool[send: 1, receive: 10];
soc: PupSocket ← PupSocketMake[fillInSocketID, where, SecondsToTocks[2]];
packetNumber: CARDINAL ← GetNextSequenceNumber[];
FOR i: CARDINAL IN [0..10) UNTIL pleaseStop DO
b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
b.pup.pupID.a ← b.pup.pupID.b ← packetNumber;
b.pup.pupType ← bootDirReq;
SetPupContentsWords[b, 0];
soc.put[b];
UNTIL (b ← soc.get[]) = NIL DO
SELECT TRUE FROM
((b.pup.pupType # bootDirReply) OR (b.pup.pupID.a # packetNumber)
OR (b.pup.pupID.b # packetNumber)) =>
BEGIN
temp: STRING = [100];
PupDefs.AppendErrorPup[temp, b];
MsgSW.Post[msg, temp];
END;
ENDCASE => BEGIN FlipBoxes[]; LookAtBootDir[b]; END;
Buffer.ReturnBuffer[b];
ENDLOOP;
IF b # NIL THEN Buffer.ReturnBuffer[b];
ENDLOOP;
PupSocketDestroy[soc];
Buffer.DestroyPool[pool];
END;
LookAtBootDir: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN OPEN PupDefs;
word, size, end: CARDINAL;
end ← GetPupContentsBytes[b]/2;
word ← 0;
UNTIL word >= end DO
name: STRING = [256];
timeStamp: System.GreenwichMeanTime;
timeStampLocation: LONG POINTER TO PupWireFormat.BcplLongNumber;
timeStampLocation ← LOOPHOLE[@b.pup.pupWords[word + 1]];
timeStamp ← LOOPHOLE[PupWireFormat.BcplToMesaLongNumber[
timeStampLocation↑]];
CopyBcplString[name, LOOPHOLE[@b.pup.pupWords[word + 1 + 2]]];
AddToList[b.pup.pupWords[word], b.pup.source, timeStamp, name];
size ← ((1 + name.length) + 1)/2;
word ← word + size + 1 + 2;
ENDLOOP;
END;
CopyBcplString: PROCEDURE [
a: LONG STRING, b: LONG POINTER TO PupWireFormat.BcplSTRING] =
BEGIN
a.length ← b.length;
FOR i: CARDINAL IN [0..b.length) DO a[i] ← b.char[i]; ENDLOOP;
END;
AddToList: PROCEDURE [
number: WORD, where: PupTypes.PupAddress, date: System.GreenwichMeanTime,
name: LONG STRING] =
BEGIN
finger: BootFile ← NIL;
new: BootFile;
FOR bf: BootFile ← first, bf.next UNTIL bf = NIL DO
IF number = bf.number AND where = bf.where AND date = bf.date
AND String.Equivalent[name, bf.name] THEN RETURN;
IF bf.number < number OR (bf.number = number AND bf.date < date)
OR
(bf.number = number AND bf.date = date
AND LessPupAddress[bf.where, where]) THEN finger ← bf;
ENDLOOP;
new ← Node[SIZE[BootFileInfo]];
new↑ ← [
next: NIL, number: number, where: where, date: date, name: ];
IF finger # NIL AND String.Equivalent[name, finger.name] THEN
new.name ← finger.name
ELSE new.name ← CopyString[name];
SELECT TRUE FROM
first = NIL => first ← new; -- first
finger = NIL => BEGIN new.next ← first; first ← new; END; -- insert at front of list
ENDCASE => BEGIN new.next ← finger.next; finger.next ← new; END; -- middle or end
END;
LessPupAddress: PROCEDURE [a, b: PupAddress] RETURNS [BOOLEAN] =
BEGIN
IF a.net < b.net THEN RETURN[TRUE];
IF a.net > b.net THEN RETURN[FALSE];
IF a.host < b.host THEN RETURN[TRUE];
IF a.host > b.host THEN RETURN[FALSE];
IF a.socket.a < b.socket.a THEN RETURN[TRUE];
IF a.socket.a > b.socket.a THEN RETURN[FALSE];
IF a.socket.b < b.socket.b THEN RETURN[TRUE];
IF a.socket.b > b.socket.b THEN RETURN[FALSE];
RETURN[FALSE];
END;
PrintBootFileList: PROCEDURE =
BEGIN
bf: BootFile ← first;
UNTIL bf = NIL DO
temp: STRING = [40];
PupDefs.AppendHostName[temp, bf.where];
Put.Number[log, bf.number, [8, FALSE, TRUE, 6]];
Put.Text[log, " "L];
Put.Date[log, bf.date, dateTime];
Put.Text[log, " "L];
Put.Text[log, bf.name];
THROUGH [bf.name.length..25) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, temp];
FOR bf2: BootFile ← bf.next, bf2.next UNTIL bf2 = NIL DO
temp: STRING = [40];
IF bf.number # bf2.number OR bf.date # bf2.date
OR ~String.Equivalent[bf2.name, bf.name] THEN EXIT;
PupDefs.AppendHostName[temp, bf2.where];
Put.Text[log, ", "L];
Put.Text[log, temp];
bf ← bf2;
ENDLOOP;
Put.CR[log];
bf ← bf.next;
ENDLOOP;
END;
nextSequenceNumber: CARDINAL ← 123;
GetNextSequenceNumber: PROCEDURE RETURNS [CARDINAL] =
BEGIN RETURN[nextSequenceNumber ← nextSequenceNumber + 1]; END;
-- IO things
WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[log, c]; END;
WriteCR: PROCEDURE = BEGIN Put.CR[log]; END;
WriteString: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[log, s]; END;
WriteLine: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[log, s]; END;
WriteLongDecimal: PROCEDURE [n: LONG CARDINAL] =
BEGIN Put.LongDecimal[log, n]; END;
WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END;
WriteOctal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END;
WriteNumber: 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: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 8]; END;
O3: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END;
O6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END;
O9: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END;
WriteCurrentDateAndTime: PROCEDURE =
BEGIN time: STRING = [20]; Time.AppendCurrent[time]; WriteString[time]; END;
PrintPupAddress: PROCEDURE [a: PupAddress] =
BEGIN temp: STRING = [40]; AppendPupAddress[temp, a]; WriteString[temp]; END;
indicatorBox: Window.Box = [[25, 10], [16, 16]];
DisplayBoxes: ToolWindow.DisplayProcType =
BEGIN
pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;
left: WORD = 177400B;
right: WORD = 000377B;
SELECT indicator FROM
left => pattern ← [ALL[left], ALL[right]];
right => pattern ← [ALL[right], ALL[left]];
off => pattern ← [ALL[0], ALL[0]];
ENDCASE;
Display.Bitmap[window, indicatorBox, [@pattern, 0, 0], 16, Display.replaceFlags]
END;
SetupBoxes: PROCEDURE = BEGIN indicator ← left; DisplayBoxes[boxes]; END;
FlipBoxes: PROCEDURE =
BEGIN
SELECT indicator FROM
left => indicator ← right;
off, right => indicator ← left;
ENDCASE;
Display.Invert[boxes, indicatorBox];
END;
SetDownBoxes: PROCEDURE =
BEGIN indicator ← off; Display.White[boxes, indicatorBox]; END;
MakeBoxesSW: PROCEDURE [window: Window.Handle] =
BEGIN
box: Window.Box ← ToolWindow.nullBox;
box.dims.h ← 36;
boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes, box: box];
Tool.AddThisSW[window: window, sw: boxes, swType: vanilla];
END;
MakeSWs: Tool.MakeSWsProc =
BEGIN
logFileName: STRING = [40];
msg ← Tool.MakeMsgSW[window: window, lines: 5];
form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
MakeBoxesSW[window];
Tool.UnusedLogName[logFileName, "FindBootFiles.log$"L];
log ← Tool.MakeFileSW[window: window, name: logFileName];
END;
MakeForm: FormSW.ClientItemsProcType =
BEGIN
nParams: CARDINAL = 5;
items ← FormSW.AllocateItemDescriptor[nParams];
items[0] ← FormSW.CommandItem[
tag: "Stop"L, proc: Stop, place: FormSW.newLine];
items[1] ← FormSW.CommandItem[
tag: "ScanCircle"L, proc: ScanCircle, place: FormSW.newLine];
items[2] ← FormSW.NumberItem[
tag: "MaxHops"L, value: @maxHops, default: defaultMaxHops];
items[3] ← FormSW.CommandItem[
tag: "ScanTarget"L, proc: ScanTarget, place: FormSW.newLine];
items[4] ← FormSW.StringItem[tag: "Target"L, string: @target, inHeap: TRUE];
RETURN[items, TRUE];
END;
ClientTransition: ToolWindow.TransitionProcType =
BEGIN
SELECT TRUE FROM
old = inactive =>
BEGIN
PupDefs.PupPackageMake[];
END;
new = inactive =>
BEGIN
IF running THEN Off[];
PupDefs.PupPackageDestroy[];
END;
ENDCASE;
END;
Init: PROCEDURE =
BEGIN
herald: STRING = [100];
String.AppendString[herald, "FindBootFiles of "L];
Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
[] ← Tool.Create[
name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
END;
-- Our own allocation krockery since things fill up if we use Storage OR Heap
pagesForHeap: CARDINAL = 400;
z: UNCOUNTED ZONE ← NIL;
InitializeHeap: PROCEDURE =
BEGIN
z ← Heap.Create[initial: 100, increment: 100];
END;
ZapHeap: PROCEDURE =
BEGIN
Heap.Delete[z];
InitializeHeap[];
END;
Node: PROCEDURE [n: CARDINAL] RETURNS [p: LONG POINTER] =
BEGIN
RETURN[Heap.MakeNode[z, n]];
END;
CopyString: PROCEDURE [s: LONG STRING] RETURNS [LONG STRING] =
BEGIN
RETURN[String.CopyToNewString[s, z]];
END;
InitializeHeap[];
Init[];
END.