-- File: PupBootServerCold.mesa, Last Edit: HGM March 19, 1981 4:19 AM
DIRECTORY
Inline USING [LowHalf],
InlineDefs USING [BcplLongNumber, BcplToMesaLongNumber, MesaToBcplLongNumber],
Process USING [Detach, SetTimeout, MsecToTicks, Pause, Yield],
Runtime USING [IsBound],
Storage USING [CopyString, Free, FreeString, Node],
String USING [
AppendChar, AppendLongNumber, AppendNumber, AppendString,
EquivalentString, InvalidNumber,
StringToOctal, SubString, SubStringDescriptor],
StringDefs USING [BcplSTRING],
System USING [
GetClockPulses, GreenwichMeanTime, GetGreenwichMeanTime,
Pulses, PulsesToMicroseconds],
Time USING [AppendCurrent, Current, Append, Unpack],
File USING [Capability, nullCapability],
Space USING [nullHandle],
CmFile USING [Close, GetNextToken, NextItem, OpenSection],
Event USING [Item, Reason, AddNotifier],
Put USING [Line],
Window USING [Handle],
StatsDefs USING [StatCounterIndex, StatIncr, StatGetCounter],
Clock USING [TimeIsKnown],
George USING [
CreateInputStream, CreateOutputStream, Destroy, DeleteFileFromDisk,
EnumerateDirectory, GetWords, GetLength, Handle, NameToCapability, PutWords,
SetIndex],
Indirect USING [GetParmFileName],
BootServerDefs USING [
BootFile, BootFileObject, BootFileHeader, timeNotKnown, GetPointerToBootTable,
SendBootDir, BreatherOn, BreatherOff, KillSpace, PupBootServer, UpdatePicture,
BootStatsEntry, bootStatsReply, bootVersion],
MiscServerDefs USING [
PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetBootServer],
Slosh USING [
AddProcs, CopyFile, RecvFile, RecvStatus, RetransmissionInterval, Why],
BufferDefs USING [],
DriverDefs USING [Network],
PupDefs USING [
GetFreePupBuffer, AppendHostName, PupAddress, PupBuffer, SendPup, PupSocket,
PupSocketDestroy, PupSocketID, PupSocketMake, ReturnFreePupBuffer,
SecondsToTocks, SetPupContentsWords, GetPupContentsBytes,
UniqueLocalPupSocketID, UniqueLocalPupAddress, ReturnPup,
defaultNumberOfNetworks, GetHopsToNetwork],
PupTypes USING [miscSrvSoc, PupNetID, allNets, allHosts];
PupBootServerCold: MONITOR LOCKS lock
IMPORTS
Inline, InlineDefs, Process, Runtime, Storage, String, System, Time, Space,
CmFile, Event, Put, Clock, George, Indirect, StatsDefs, BootServerDefs,
MiscServerDefs, Slosh, PupDefs
EXPORTS BufferDefs, BootServerDefs
SHARES BufferDefs =
BEGIN OPEN BootServerDefs;
-- EXPORTed TYPEs
Network: PUBLIC TYPE = DriverDefs.Network;
lock: PUBLIC MONITORLOCK;
useCount: CARDINAL ← 0;
running, booting, slowBooting, pleaseStop, probing, sloshing: PUBLIC BOOLEAN ←
FALSE;
longRangeMode: BOOLEAN ← FALSE;
msg: PUBLIC Window.Handle ← NIL;
slosheeHost, slosheeFileName: PUBLIC STRING ← NIL;
first: POINTER TO BootFile ← GetPointerToBootTable[];
probeTicks: CARDINAL ← 1*3600/5; -- 1 hour
eventItem: Event.Item ← [eventMask: 177777B, eventProc: Broom];
statLife: PUBLIC StatsDefs.StatCounterIndex;
statBootNew: PUBLIC StatsDefs.StatCounterIndex;
statBootDir: PUBLIC StatsDefs.StatCounterIndex;
statFile: PUBLIC StatsDefs.StatCounterIndex;
statFileSent: PUBLIC StatsDefs.StatCounterIndex;
statFileSentSlow: PUBLIC StatsDefs.StatCounterIndex;
statFileTroubles: PUBLIC StatsDefs.StatCounterIndex;
statFileNeverStarted: PUBLIC StatsDefs.StatCounterIndex;
statBusyDisk: PUBLIC StatsDefs.StatCounterIndex;
statBusyBooting: PUBLIC StatsDefs.StatCounterIndex;
statMicrocodeBooted: PUBLIC StatsDefs.StatCounterIndex;
statUnknown: PUBLIC StatsDefs.StatCounterIndex;
verbose: BOOLEAN = TRUE;
PupBootServerOn: PUBLIC PROCEDURE =
BEGIN
IF (useCount ← useCount + 1) = 1 THEN BEGIN running ← TRUE; Starter[]; END;
FixupPicture[];
END;
Starter: PROCEDURE =
BEGIN
pleaseStop ← FALSE;
[] ← FindBootFiles[];
SweepDirectory[];
CheckDates[];
PrintMissingFiles[];
MiscServerDefs.PupMiscServerOn[];
MiscServerDefs.SetBootServer[BootServerDefs.PupBootServer];
Process.Detach[FORK BootServerDefs.BreatherOn[]];
Process.Detach[FORK Prober[]];
END;
PupBootServerOff: PUBLIC PROCEDURE =
BEGIN
IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
BEGIN running ← FALSE; Stopper[]; END;
FixupPicture[];
END;
Stopper: PROCEDURE =
BEGIN
pleaseStop ← TRUE;
MiscServerDefs.SetBootServer[MiscServerDefs.IgnoreThisPacket];
BreatherOff[];
WHILE booting OR slowBooting OR probing OR sloshing DO
Process.Pause[Process.MsecToTicks[100]]; ENDLOOP;
MiscServerDefs.PupMiscServerOff[];
ForgetBootFiles[];
END;
FixupPicture: PROCEDURE =
BEGIN
IF msg # NIL AND Runtime.IsBound[UpdatePicture] THEN UpdatePicture[];
END;
Newer: PROCEDURE [him, me: System.GreenwichMeanTime] RETURNS [BOOLEAN] =
BEGIN
IF him = BootServerDefs.timeNotKnown THEN RETURN[FALSE];
RETURN[him > me]; -- FIX THIS FOR EPOC STUFF
END;
--For more on boot files see the BuildBoot documentation
-- Boot files created after late Dec 78 have a time stamp stored in words 3+4.
-- OutLd leaves it zero. GateControl smashes it to "now".
--There are two kinds of boot file.
--B-Files produced by BuildBoot.run:
-- File page 1: DiskBoot loader
-- File page 2: locations #0-#377
-- File page 3: locations #1000 - #1377
-- File page 4: locations #1400 - #1777
-- ...
-- File page n: locations #(n-1)B7 - #(n-1)B7+#377
--B-Files have 0 in the second data word
--B-Files are started by jmp @0 when loading is complete
--S-Files produced by Swat OutLd:
-- File page 1: Special loader
-- File page 2: locations #1000 - #1377
-- File page 3: locations #1400 - #1777
-- ...
-- File page 253: locations #176400-176777
-- File page 254: locations #400 - #777
-- File page 255: locations #0 - #377
--S-Files have a non-zero value in the second data word
--Some S-Files can be started by jmp @0.
--This is the kind we use, but we re-format them first
-- This routine converts an S-format file into a B-format file if necessary. There are problems with Sys.boot. For obvious reasons, we don't want to "fix" it.
BlessBootFile: PROCEDURE [bf: BootFile] =
BEGIN
old, new: George.Handle ← NIL;
buffer: ARRAY [0..256) OF WORD;
scratchName: STRING = [40];
scratch: File.Capability ← File.nullCapability;
bfh: POINTER TO BootServerDefs.BootFileHeader = LOOPHOLE[@buffer];
old ← George.CreateInputStream[bf.file];
BEGIN
IF George.GetWords[old, @buffer, 256] # 256 THEN GOTO Empty;
bf.bytes ← George.GetLength[old]; -- positions to EOF
bf.pages ← Inline.LowHalf[(bf.bytes + 511)/512];
bf.create ← LOOPHOLE[InlineDefs.BcplToMesaLongNumber[bfh.timeStamp]];
IF buffer[1] # 0 THEN
BEGIN -- fixup S-format file
text: STRING = [100];
String.AppendString[scratchName, bf.fileName];
String.AppendString[scratchName, "$$$"L];
scratch ← George.NameToCapability[scratchName, 256];
Time.AppendCurrent[text];
String.AppendString[text, " Reformatting "L];
String.AppendString[text, bf.fileName];
String.AppendChar[text, '.];
LogString[text];
George.Destroy[old];
IF Slosh.CopyFile[to: scratch, from: bf.file] # statusStoreOk THEN
GOTO DiskFull;
new ← George.CreateInputStream[scratch];
old ← George.CreateOutputStream[bf.file];
IF George.GetWords[new, @buffer, 256] # 256 THEN GOTO Empty;
buffer[1] ← 0;
BEGIN -- smash crufty time stamp to now
-- Note that our clock may be wrong at this point. If it is fast, other boot servers will ignore our file. If it is slow, we will go get a new boot file. I think that will work out ok.
bfh.timeStamp ← InlineDefs.MesaToBcplLongNumber[Time.Current[]];
bf.create ← LOOPHOLE[InlineDefs.BcplToMesaLongNumber[bfh.timeStamp]];
END;
George.PutWords[old, @buffer, 256]; -- garbage DiskBoot loader
THROUGH [2..255] DO
IF George.GetWords[new, @buffer, 256] # 256 THEN GOTO Short; ENDLOOP;
George.PutWords[old, @buffer, 256]; -- locations 0B to 377B
George.SetIndex[new, 2*256];
THROUGH [2..253] DO
[] ← George.GetWords[new, @buffer, 256];
George.PutWords[old, @buffer, 256];
ENDLOOP;
George.Destroy[new];
-- It may have shrunk if there was trash on the end of Swatee when creating a Mesa boot file
George.Destroy[old]; -- Truncate file
old ← George.CreateInputStream[bf.file];
bf.bytes ← George.GetLength[old]; -- positions to EOF
bf.pages ← Inline.LowHalf[(bf.bytes + 511)/512];
END;
EXITS
DiskFull =>
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " Disk full while copying over "L];
String.AppendString[text, bf.fileName];
LogString[text];
bf.file ← File.nullCapability;
bf.unknown ← TRUE;
END;
Empty,
Short =>
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, bf.fileName];
IF new = NIL THEN String.AppendString[text, " is empty."L]
ELSE String.AppendString[text, " is SHORT. ******"L];
LogString[text];
IF new # NIL THEN George.Destroy[new];
bf.file ← File.nullCapability;
bf.unknown ← TRUE;
END;
END;
George.Destroy[old];
IF scratch # File.nullCapability THEN George.DeleteFileFromDisk[scratch];
END;
-- This stuff should probably migrate to another file
StartProbingForBootFiles: PUBLIC ENTRY PROCEDURE =
BEGIN longRangeMode ← FALSE; ProbeForBootFiles[]; END;
StartLongRangeProbingForBootFiles: PUBLIC ENTRY PROCEDURE =
BEGIN longRangeMode ← TRUE; ProbeForBootFiles[]; END;
Prober: ENTRY PROCEDURE =
BEGIN
counter: CARDINAL ← 120/5; -- initial probe 2 min after startup
delay: CONDITION;
tryHarder: CARDINAL ← 0;
Process.SetTimeout[@delay, Process.MsecToTicks[5000]];
UNTIL pleaseStop DO
WAIT delay;
IF (counter ← counter - 1) = 0 THEN
BEGIN
tryHarder ← tryHarder + 1;
longRangeMode ← (tryHarder MOD 24) = 0;
ProbeForBootFiles[];
counter ← probeTicks;
END;
ENDLOOP;
END;
ProbeForBootFiles: INTERNAL PROCEDURE =
BEGIN
IF probing OR sloshing THEN RETURN;
probing ← TRUE;
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO bf.tries ← 0; ENDLOOP;
IF longRangeMode THEN Process.Detach[FORK LongRangeProbe[]]
ELSE Process.Detach[FORK ShortRangeProbe[]];
END;
ProbeSomeMore: ENTRY PROCEDURE =
BEGIN
IF probing OR sloshing THEN RETURN;
probing ← TRUE;
IF longRangeMode THEN Process.Detach[FORK LongRangeProbe[]]
ELSE Process.Detach[FORK ShortRangeProbe[]];
END;
ShortRangeProbe: PROCEDURE =
BEGIN OPEN PupDefs;
FixupPicture[];
ProbeOne[PupTypes.allNets];
probing ← FALSE;
FixupPicture[];
IF ~sloshing THEN BootServerDefs.SendBootDir[NIL];
END;
LongRangeProbe: PROCEDURE =
BEGIN
FixupPicture[];
FOR net: CARDINAL IN [1..PupDefs.defaultNumberOfNetworks) DO
IF PupDefs.GetHopsToNetwork[[net]] > 3 THEN LOOP;
ProbeOne[[net]];
IF sloshing THEN EXIT;
ENDLOOP;
probing ← FALSE;
FixupPicture[];
IF ~sloshing THEN BootServerDefs.SendBootDir[NIL];
END;
ProbeOne: PROCEDURE [net: PupTypes.PupNetID] =
BEGIN OPEN PupDefs;
b: PupBuffer;
from: PupSocketID ← UniqueLocalPupSocketID[];
socket: PupSocket;
socket ← PupSocketMake[
from, [net, PupTypes.allHosts, PupTypes.miscSrvSoc], SecondsToTocks[2]];
THROUGH [0..5) UNTIL pleaseStop OR sloshing DO
b ← GetFreePupBuffer[];
b.pupType ← bootDirReq;
SetPupContentsWords[b, 0];
socket.put[b];
UNTIL pleaseStop OR sloshing DO
b ← socket.get[];
IF b = NIL THEN EXIT;
IF b.pupType # bootDirReply THEN BEGIN ReturnFreePupBuffer[b]; LOOP; END;
LookAtBootDir[b];
ENDLOOP;
ENDLOOP;
PupSocketDestroy[socket];
END;
LookAtBootDir: PUBLIC ENTRY PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN OPEN PupDefs;
bf: BootFile ← NIL;
where: PupAddress ← b.source;
name: STRING = [256];
word, size, end: CARDINAL;
timeStamp: System.GreenwichMeanTime;
timeStampLocation: LONG POINTER TO InlineDefs.BcplLongNumber;
now: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
heNeedsOne: BOOLEAN ← FALSE;
IF pleaseStop THEN GOTO Stopping;
IF sloshing THEN GOTO AlreadySloshing;
BEGIN
network: Network ← b.network;
IF network.netNumber.b = b.source.net AND network.hostNumber = b.source.host
THEN GOTO FromMe;
END;
end ← GetPupContentsBytes[b]/2;
word ← 0;
UNTIL word >= end OR bf # NIL DO
timeStampLocation ← LOOPHOLE[@b.pupWords[word + 1]];
timeStamp ← LOOPHOLE[InlineDefs.BcplToMesaLongNumber[timeStampLocation↑]];
StringCopy[LOOPHOLE[@b.pupWords[word + 1 + 2]], name];
size ← ((1 + name.length) + 1)/2;
FOR bf ← first↑, bf.next UNTIL bf = NIL DO
IF bf.code >= 100000B THEN LOOP;
IF bf.inTransit THEN LOOP;
IF bf.code # b.pupWords[word] THEN LOOP;
IF ~String.EquivalentString[name, bf.fileName] THEN LOOP;
IF Newer[timeStamp, now] THEN LOOP; -- Don't propagate garbage
IF ~bf.unknown AND bf.create #
BootServerDefs.timeNotKnown AND Newer[bf.create, timeStamp] THEN
heNeedsOne ← TRUE;
IF ~bf.unknown AND ~Newer[timeStamp, bf.create] THEN LOOP;
IF bf.tries > 1 THEN LOOP;
EXIT;
ENDLOOP;
word ← word + size + 1 + 2;
ENDLOOP;
IF ~Clock.TimeIsKnown[] THEN GOTO TimeNotKnown;
IF sloshing THEN GOTO Sloshing;
-- This is just a hack to let him get the new version sooner. It should all work ok without this code.
IF heNeedsOne THEN
BEGIN
b.dest.socket ← PupTypes.miscSrvSoc;
BootServerDefs.SendBootDir[b];
END
ELSE ReturnFreePupBuffer[b];
-- Don't call AppendHostName (or friends) from here. The lock is still locked.
IF bf # NIL THEN
BEGIN
sloshing ← TRUE;
bf.tries ← bf.tries + 1;
Process.Detach[FORK GetNewBootFile[bf, where]];
RETURN;
END;
EXITS
AlreadySloshing,
FromMe,
Stopping,
Sloshing,
TimeNotKnown => ReturnFreePupBuffer[b];
END;
StringCopy: PROCEDURE [s: LONG POINTER TO StringDefs.BcplSTRING, d: STRING] =
BEGIN
i: CARDINAL;
d.length ← s.length;
FOR i IN [0..s.length) DO d[i] ← s.char[i]; ENDLOOP;
END;
GetNewBootFile: PROCEDURE [bf: BootFile, where: PupDefs.PupAddress] =
BEGIN OPEN PupDefs;
hisName: STRING = [30];
from: PupAddress ← UniqueLocalPupAddress[@where];
status: Slosh.RecvStatus;
Ask: PROCEDURE =
BEGIN
b: PupBuffer ← GetFreePupBuffer[];
b.source ← from;
b.dest ← where;
b.pupID ← [0, bf.code];
SendPup[b, bootFileSend, 0];
END;
AppendHostName[hisName, where];
slosheeHost ← hisName;
slosheeFileName ← bf.fileName;
FixupPicture[];
IF verbose THEN StartupMessage[bf, where];
DoSomeYields[];
status ← Slosh.RecvFile[
msg, bf.fileName, "Boot.scratch$"L, bf.file, from, Ask];
IF status # statusStoreOk THEN
BEGIN
n: CARDINAL ← Slosh.RetransmissionInterval[];
IF status = statusDiskFull THEN bf.tries ← bf.tries + 1;
THROUGH [0..n) UNTIL pleaseStop DO
Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP;
END;
sloshing ← FALSE;
slosheeHost ← slosheeFileName ← NIL;
FixupPicture[];
IF pleaseStop OR probing THEN RETURN;
DoSomeYields[];
ProbeSomeMore[];
END;
StartupMessage: PROCEDURE [bf: BootFile, where: PupDefs.PupAddress] =
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " Found "L];
IF ~bf.unknown THEN String.AppendString[text, "newer version of "L];
String.AppendString[text, bf.fileName];
String.AppendString[text, " (#"L];
String.AppendNumber[text, bf.code, 8];
String.AppendString[text, ") on "L];
PupDefs.AppendHostName[text, where];
String.AppendChar[text, '.];
LogString[text];
END;
Checker: PROCEDURE [why: Slosh.Why, fileName: STRING, file: File.Capability] =
BEGIN
SELECT why FROM
check =>
BEGIN
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF String.EquivalentString[bf.fileName, fileName] THEN
BEGIN -- inspect things here if we can think of anything to do
END;
ENDLOOP;
END;
release =>
BEGIN
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF bf.file = file THEN
BEGIN
IF bf.space # Space.nullHandle THEN KillSpace[bf];
bf.file ← File.nullCapability;
bf.unknown ← TRUE;
bf.inTransit ← TRUE;
END;
ENDLOOP;
END;
arrived =>
BEGIN
parmFileName: STRING ← NIL;
IF Runtime.IsBound[Indirect.GetParmFileName] THEN
parmFileName ← Indirect.GetParmFileName[];
IF parmFileName = NIL THEN parmFileName ← "BootServer.txt"L;
IF String.EquivalentString[parmFileName, fileName] AND running THEN
BEGIN
text: STRING = [150];
Time.AppendCurrent[text];
String.AppendString[
text, " BootServer restarting because a new version of "L];
String.AppendString[text, parmFileName];
String.AppendString[text, " arrived."L];
LogString[text];
DoSomeYields[];
Stopper[];
Starter[];
END;
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF String.EquivalentString[bf.fileName, fileName] THEN
BEGIN
text: STRING = [150];
StatsDefs.StatIncr[statBootNew];
-- could check to be sure bf.file=File.nullCapability
bf.file ← file;
bf.unknown ← FALSE;
BlessBootFile[bf];
SendBootDir[NIL];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, bf.fileName];
String.AppendString[text, " (#"L];
String.AppendNumber[text, bf.code, 8];
String.AppendString[text, ") was created on "L];
IF bf.create = BootServerDefs.timeNotKnown THEN
String.AppendString[text, "???"L]
ELSE Time.Append[text, Time.Unpack[bf.create]];
String.AppendChar[text, '.];
LogString[text];
DoSomeYields[];
bf.inTransit ← FALSE;
END;
ENDLOOP;
END;
failed =>
BEGIN
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF String.EquivalentString[bf.fileName, fileName] THEN
BEGIN bf.inTransit ← FALSE; END;
ENDLOOP;
END;
ENDCASE => ERROR;
RETURN;
END;
BootServerStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN OPEN StatsDefs;
BcplCounter: PROCEDURE [s: StatCounterIndex]
RETURNS [InlineDefs.BcplLongNumber] =
BEGIN RETURN[InlineDefs.MesaToBcplLongNumber[StatGetCounter[s]]]; END;
bse: LONG POINTER TO BootStatsEntry ← LOOPHOLE[@b.pupWords];
bse↑ ←
[version: bootVersion, directories: BcplCounter[statBootDir],
fastSends: BcplCounter[statFileSent],
slowSends: BcplCounter[statFileSentSlow],
filesRecv: BcplCounter[statBootNew]];
PupDefs.ReturnPup[b, bootStatsReply, 2*SIZE[BootStatsEntry]];
END;
ParameterError: ERROR [s: STRING] = CODE;
GetOctal: PROCEDURE [ss: String.SubString] RETURNS [CARDINAL] =
BEGIN
ENABLE
String.InvalidNumber => ERROR ParameterError["Octal number expected: "L];
token: STRING = [100];
IF ~CmFile.GetNextToken[ss, token] THEN SIGNAL String.InvalidNumber;
RETURN[String.StringToOctal[token]];
END;
GetName: PROCEDURE [ss: String.SubString] RETURNS [STRING] =
BEGIN
token: STRING = [100];
IF ~CmFile.GetNextToken[ss, token] THEN
ERROR ParameterError["Another Token expected: "L];
RETURN[Storage.CopyString[token]];
END;
FindBootFiles: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
sectionName: STRING = "BootServer"L;
parmFileName: STRING ← NIL;
IF Runtime.IsBound[Indirect.GetParmFileName] THEN
parmFileName ← Indirect.GetParmFileName[];
IF parmFileName = NIL THEN parmFileName ← "BootServer.txt"L;
IF ~CmFile.OpenSection[parmFileName, sectionName] THEN
BEGIN
Message["Can't find [BootServer] section in "L, parmFileName];
RETURN[FALSE];
END;
DO
ss: String.SubStringDescriptor;
name, arg: STRING ← NIL;
text: STRING = [200];
[name, arg] ← CmFile.NextItem[];
IF arg # NIL THEN ss ← [base: arg, offset: 0, length: arg.length];
IF name = NIL THEN EXIT;
IF name[0] = '; THEN {
Storage.FreeString[name]; Storage.FreeString[arg]; LOOP; };
BEGIN
ENABLE ParameterError => BEGIN Message[s, name, ": "L, arg]; CONTINUE; END;
code: WORD ← GetOctal[@ss];
fileName: STRING ← GetName[@ss];
String.AppendString[text, "Boot file number "L];
String.AppendNumber[text, code, 8];
String.AppendString[text, " is "L];
String.AppendString[text, fileName];
String.AppendString[text, "."L];
LogString[text];
AddBootFile[code, fileName];
END;
Storage.FreeString[name];
Storage.FreeString[arg];
ENDLOOP;
CmFile.Close[parmFileName];
RETURN[TRUE];
END;
AddBootFile: PROCEDURE [code: WORD, fileName: STRING] =
BEGIN
bf: BootFile ← Storage.Node[SIZE[BootFileObject]];
last: BootFile;
bf↑ ←
[next: NIL, code: code, create: BootServerDefs.timeNotKnown,
file: File.nullCapability, fileName: fileName, ms: 0, count: 0, pages: 0,
bytes: 0, space: Space.nullHandle, tries: 0, unknown:TRUE, inTransit: TRUE];
IF first↑ = NIL THEN first↑ ← bf
ELSE
BEGIN
FOR last ← first↑, last.next UNTIL last.next = NIL DO ENDLOOP;
last.next ← bf;
END;
END;
SweepDirectory: PROCEDURE =
BEGIN
pulses: System.Pulses;
files: CARDINAL ← 0;
text: STRING = [200];
CheckOne: PROCEDURE [file: File.Capability, name: STRING] RETURNS [BOOLEAN] =
BEGIN
files ← files+1;
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF String.EquivalentString[bf.fileName, name] THEN
BEGIN
bf.file ← file;
bf.unknown ← FALSE;
END;
ENDLOOP;
RETURN[FALSE]; -- the same name may be in more than one slot
END;
pulses ← System.GetClockPulses[];
George.EnumerateDirectory[CheckOne];
pulses ← System.Pulses[System.GetClockPulses[] - pulses];
String.AppendString[text, "It took "L];
String.AppendLongNumber[text, System.PulsesToMicroseconds[pulses]/1000, 10];
String.AppendString[text, " ms to scan the directory which contained "L];
String.AppendNumber[text, files, 10];
String.AppendString[text, " files."L];
LogString[text];
DoSomeYields[];
END;
CheckDates: PROCEDURE =
BEGIN
pulses: System.Pulses;
files: CARDINAL ← 0;
text: STRING = [200];
pulses ← System.GetClockPulses[];
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF ~bf.unknown THEN BlessBootFile[bf];
bf.inTransit ← FALSE;
files ← files+1;
DoSomeYields[];
ENDLOOP;
pulses ← System.Pulses[System.GetClockPulses[] - pulses];
String.AppendString[text, "It took "L];
String.AppendLongNumber[text, System.PulsesToMicroseconds[pulses]/1000, 10];
String.AppendString[text, " ms to check the dates in "L];
String.AppendNumber[text, files, 10];
String.AppendString[text, " files."L];
LogString[text];
DoSomeYields[];
END;
PrintMissingFiles: PROCEDURE =
BEGIN
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO
IF bf.unknown THEN
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " BootServer: "L];
String.AppendString[text, bf.fileName];
String.AppendString[text, " (#"L];
String.AppendNumber[text, bf.code, 8];
String.AppendString[text, ") is not on this disk."L];
LogString[text];
DoSomeYields[];
END;
ENDLOOP;
END;
ForgetBootFiles: PROCEDURE =
BEGIN
UNTIL first↑ = NIL DO
bf: BootFile ← first↑;
first↑ ← bf.next;
IF bf.space # Space.nullHandle THEN KillSpace[bf];
Storage.FreeString[bf.fileName];
Storage.Free[bf];
ENDLOOP;
END;
EnumerateBootTable: PUBLIC ENTRY PROCEDURE [proc: PROCEDURE [BootFile]] =
BEGIN
FOR bf: BootFile ← first↑, bf.next UNTIL bf = NIL DO proc[bf]; ENDLOOP;
END;
Message: PROCEDURE [s1, s2, s3, s4: STRING ← NIL] =
BEGIN
text: STRING = [200];
String.AppendString[text, "BootServer: "L];
String.AppendString[text, s1];
IF s2 # NIL THEN String.AppendString[text, s2];
IF s3 # NIL THEN String.AppendString[text, s3];
IF s4 # NIL THEN String.AppendString[text, s4];
String.AppendChar[text, '.];
LogString[text];
END;
LogString: PROCEDURE [text: STRING] =
BEGIN IF msg # NIL THEN Put.Line[msg, text]; Put.Line[NIL, text]; END;
DoSomeYields: PROCEDURE =
BEGIN THROUGH [0..100) DO Process.Yield[]; ENDLOOP; END;
Broom: PROCEDURE [why: Event.Reason] =
BEGIN
SELECT why FROM
makeImage, makeCheck, stopMesa => IF running THEN Stopper[];
startImage, restartCheck, continueCheck => IF running THEN Starter[];
ENDCASE => NULL;
END;
-- initialization
Event.AddNotifier[@eventItem];
Slosh.AddProcs[Checker];
END.