-- Transport Mechanism Registration Server - restart sequence.
-- [Indigo]<Grapevine>MS>RegRestart.mesa
-- Randy Gobbel, 20-May-81 10:37:16
-- Andrew Birrell, 29-Oct-82 10:16:19
-- Mike Schroeder 25-Jan-83 15:48:25
DIRECTORY
BodyDefs USING [maxRNameLength, oldestTime, Password, RName,
Timestamp],
EnquiryDefs USING [],
HeapDefs USING [HeapAbandonWrite, HeapEndWrite, HeapEndRead,
HeapReadData, HeapReadRName, HeapStartRead,
HeapStartWrite, ObjectNumber,
ReceiveComponent, ReaderHandle, ReadRList,
WriterHandle],
LocalNameDefs USING[ ReadRSName ],
LocateDefs USING[ FindNearestServer, FindRegServer, FoundServerInfo ],
LogDefs USING[ WriteChar, WriteLine, WriteLogEntry, WriteString ],
ObjectDirDefs USING[ Enumerate, UseObject ],
PolicyDefs USING[ EndOperation, WaitOperation ],
Process USING[ Detach ],
ProtocolDefs,
PupDefs USING[ PupAddress ],
RegAccessDefs USING[ RegAccess ],
RegBTreeDefs USING[ EnumerateTree, KeepObject, Lookup, LookupReason,
MarkKnown, RegBTree, RegistryObject, RegPurger,
TestKnownReg ],
RegServerDefs USING[ AddMailbox, AddMember, AddOwner, ChangeConnect, CreateGroup,
CreateIndividual, IsMember, MailUpdate, ReadMail,
ReadMembers, Registration, RegMail, Update ],
RegistryDefs USING[ CompareTimestamps, EnumerateRList, MakeTimestamp,
ReadPrefix],
RestartDefs USING[ ],
Runtime USING[ CallDebugger ],
String USING[ AppendString, EquivalentString,
EquivalentSubStrings, SubStringDescriptor ],
Time USING[ Append, Packed, Unpack ];
RegRestart: PROGRAM[ initHeap: BOOLEAN ]
IMPORTS HeapDefs, LocalNameDefs, LocateDefs, LogDefs, ObjectDirDefs,
PolicyDefs, Process, ProtocolDefs, RegAccessDefs, RegBTreeDefs,
RegServerDefs, RegistryDefs, Runtime, String, Time
EXPORTS EnquiryDefs--AddRegistry--, RestartDefs --PROGRAM-- =
BEGIN
EndsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] =
BEGIN
pattern: String.SubStringDescriptor ← [b, 0, b.length];
target: String.SubStringDescriptor ← [s,s.length-b.length,b.length];
RETURN[ s.length >= b.length
AND String.EquivalentSubStrings[@pattern,@target] ]
END;
maxDownTime: CARDINAL ← 4 -- days --;
WaitForTime: PROC[then: BodyDefs.Timestamp] =
BEGIN
log: STRING = [64];
futureLimit: Time.Packed ←
LOOPHOLE[then.time + (LONG[maxDownTime] * 24) * 60 * 60];
-- Note: "CompareTimestamps" treats very future times as zero --
IF RegistryDefs.CompareTimestamps[then, RegistryDefs.MakeTimestamp[]]
# less
THEN BEGIN
String.AppendString[log, "Current time is less than "L];
Time.Append[log, Time.Unpack[then.time]];
LogDefs.WriteLogEntry[log];
Runtime.CallDebugger[log];
END;
IF RegistryDefs.MakeTimestamp[].time > futureLimit
THEN BEGIN
log.length ← 0;
String.AppendString[log, "Current time is too long after "L];
Time.Append[log, Time.Unpack[then.time]];
LogDefs.WriteLogEntry[log];
Runtime.CallDebugger[log];
END;
END;
FindKnownRegistries: PROC =
BEGIN
myName: BodyDefs.RName = LocalNameDefs.ReadRSName[].name;
CheckLocalRegistry: PROC[name: BodyDefs.RName] =
BEGIN
IF MyRegistry[myName, name]
THEN { LogDefs.WriteString["Known registry "L];
LogDefs.WriteString[name];
LogDefs.WriteString["; "L];
SIGNAL RegBTreeDefs.MarkKnown[] };
END;
RegBTreeDefs.EnumerateTree[group, CheckLocalRegistry];
END;
MyRegistry: PROC[myName, group: BodyDefs.RName] RETURNS[ BOOLEAN ] = INLINE
{ RETURN[ RegServerDefs.IsMember[group, myName, direct].membership =
yes ] };
baseOfWorld: BodyDefs.RName = "GV.GV";
InitializeFromLocalHeap: PROCEDURE RETURNS[limit: BodyDefs.Timestamp] =
BEGIN
registries: BOOLEAN ← TRUE; -- registries on first pass, then others --
RestartObject: PROCEDURE [object: HeapDefs.ObjectNumber] RETURNS [BOOLEAN] =
BEGIN
oldRegObj: RegBTreeDefs.RegistryObject;
newReader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[object];
newName: BodyDefs.RName = [BodyDefs.maxRNameLength];
newType: ProtocolDefs.RNameType;
newStamp: BodyDefs.Timestamp;
[newType, newStamp] ← RegistryDefs.ReadPrefix[newReader, newName];
IF EndsWith[newName,".GV"L] = registries
THEN BEGIN
-- first pass: only names ending with ".gv"
-- second pass: only names not ending with ".gv"
IF registries OR RegBTreeDefs.TestKnownReg[newName] = yes
THEN BEGIN
IF RegistryDefs.CompareTimestamps[newStamp, limit] = greater
THEN limit ← newStamp;
oldRegObj ← RegBTreeDefs.Lookup[newName, readNone];
IF oldRegObj.type = notFound
OR RegistryDefs.CompareTimestamps[oldRegObj.stamp,newStamp] = less
THEN RegBTreeDefs.KeepObject[newName, newType, @newStamp, object];
IF oldRegObj.reader # NIL
THEN HeapDefs.HeapEndRead[oldRegObj.reader];
END
ELSE LogDiscard[newName];
END;
HeapDefs.HeapEndRead[newReader];
RETURN [FALSE]; -- ie keep enumerating
END;
limit ← BodyDefs.oldestTime;
[] ← ObjectDirDefs.Enumerate[RSobject, RestartObject];
FindKnownRegistries[];
registries ← FALSE;
[] ← ObjectDirDefs.Enumerate[RSobject, RestartObject];
limit ← RecoverRSMailObjects[limit];
WaitForTime[limit];
END;
LogDiscard: PROC[name: BodyDefs.RName] =
BEGIN
log: STRING = [80];
String.AppendString[log, "Unknown: "L];
String.AppendString[log, name];
LogDefs.WriteLogEntry[log];
LogDefs.WriteChar[' ];
LogDefs.WriteString[log];
END;
initializeWorldCalled: BOOLEAN ← FALSE;
InitializeWorld: PROCEDURE =
BEGIN
OPEN RegServerDefs;
-- Must be called only if this is the first R-Server in the world --
wizard: BodyDefs.RName = "Wizard.gv"L;
firstRS: BodyDefs.RName = "FirstRS.gv"L;
msReg: BodyDefs.RName = "MS.gv"L;
deadLetter: BodyDefs.RName = "DeadLetter.ms"L;
firstMS: BodyDefs.RName = "FirstMS.ms"L;
maildrop: BodyDefs.RName = "MailDrop.ms"L;
foreignReg: BodyDefs.RName = "Foreign.gv"L;
[] ← CreateGroup[baseOfWorld, wizard]; -- GV.GV
[] ← AddMember[baseOfWorld, firstRS];
[] ← CreateIndividual[wizard, ProtocolDefs.MakeKey["grapevine"L]];
[] ← CreateIndividual[firstRS, ProtocolDefs.MakeKey["grapevine"L]];
[] ← ChangeConnect[firstRS, "ME"L];
RESTART RegServerDefs.Registration; -- in "local" mode --
FindKnownRegistries[];
[] ← CreateGroup[msReg, wizard]; -- create "MS" registry
[] ← AddMember[msReg, firstRS]; -- known to this R-Server
[] ← CreateGroup[deadLetter, wizard]; -- deadLetter.ms for "return-to" in update mail
[] ← CreateIndividual[firstMS, ProtocolDefs.MakeKey["grapevine"L]]; -- MS-name
[] ← ChangeConnect[firstMS, "ME"L];
[] ← CreateGroup[maildrop, wizard];
[] ← AddMailbox[firstRS, firstMS];
[] ← AddMailbox[firstMS, firstMS];
[] ← AddMailbox[wizard, firstMS];
[] ← AddMember[deadLetter, wizard];
[] ← AddMember[maildrop, firstMS];
[] ← CreateGroup[foreignReg, wizard]; -- create "Foreign" registry
[] ← AddMember[foreignReg, firstRS]; -- known to this R-Server
[] ← AddOwner[baseOfWorld, wizard];
[] ← AddOwner[msReg, wizard];
[] ← AddOwner[foreignReg, wizard];
initializeWorldCalled ← TRUE;
END;
ThisServerIsntInGrapevine: ERROR = CODE;
InitializeServer: PROCEDURE[myName: BodyDefs.RName,
myKey: BodyDefs.Password] =
BEGIN
-- Must be called only if this server's database is empty --
rc: ProtocolDefs.ReturnCode;
reader: HeapDefs.ReaderHandle;
oldTimePtr: BodyDefs.Timestamp ← BodyDefs.oldestTime; --ugh!--
LogDefs.WriteLine["Initializing Registration Server"L];
LogDefs.WriteLogEntry["Initializing RServer"L];
FetchRegistry[baseOfWorld, myName, myKey];
FindKnownRegistries[];
IF NOT MyRegistry[myName, baseOfWorld]
THEN ERROR ThisServerIsntInGrapevine[];
[reader, rc] ← RegServerDefs.ReadMembers["Groups.GV"L, @oldTimePtr];
IF rc # [code: done, type: group] THEN ERROR;
BEGIN
Work: PROC[name: BodyDefs.RName] RETURNS[done: BOOLEAN] =
BEGIN
done ← FALSE;
IF NOT String.EquivalentString[name, baseOfWorld]
AND MyRegistry[myName, name]
THEN FetchRegistry[name, myName, myKey];
END;
RegistryDefs.EnumerateRList[reader, Work];
HeapDefs.HeapEndRead[reader];
END;
END;
AddSelfToRegistry: PUBLIC PROC[name: BodyDefs.RName]
RETURNS[done: BOOLEAN] =
BEGIN
IF RegServerDefs.IsMember["*.gv", name, direct].membership # yes
THEN RETURN[FALSE];
IF RegServerDefs.AddMember[name, LocalNameDefs.ReadRSName[].name]
# [done, group]
THEN RETURN[FALSE];
RETURN[TRUE]
END;
AddRegistry: PUBLIC PROC[name: BodyDefs.RName]
RETURNS[done: BOOLEAN] =
BEGIN
myName: BodyDefs.RName;
myKey: BodyDefs.Password;
[myName,,myKey] ← LocalNameDefs.ReadRSName[];
IF NOT MyRegistry[myName, name] THEN RETURN[FALSE];
done ← TRUE;
FetchRegistry[name, myName, myKey !
CantFetchRegistry => {done ← FALSE; CONTINUE} ];
END;
CantFetchRegistry: ERROR[name: BodyDefs.RName] = CODE;
FetchRegistry: PROCEDURE[name, myName: BodyDefs.RName,
myKey: BodyDefs.Password] =
BEGIN
str: ProtocolDefs.Handle ← NIL;
AcceptNonLocal: PROCEDURE[addr: PupDefs.PupAddress]RETURNS[ BOOLEAN ] =
BEGIN
IF ProtocolDefs.IsLocal[addr]
THEN RETURN[FALSE]
ELSE BEGIN
addr.socket ← ProtocolDefs.RegServerEnquirySocket;
str ← ProtocolDefs.CreateStream[addr: addr, secs: 600 !
ProtocolDefs.Failed => GOTO no ];
RETURN[TRUE];
EXITS no => RETURN[FALSE]
END;
END;
BEGIN
ENABLE UNWIND => IF str # NIL THEN str.delete[str];
info: LocateDefs.FoundServerInfo =
LocateDefs.FindNearestServer[name, AcceptNonLocal];
LogDefs.WriteString["FetchRegistry: "L];
LogDefs.WriteLine[name];
WITH info SELECT FROM
notFound, allDown => ERROR CantFetchRegistry[name];
found =>
BEGIN
ENABLE ProtocolDefs.Failed => ERROR CantFetchRegistry[name];
BEGIN
rc: ProtocolDefs.ReturnCode;
ProtocolDefs.SendRSOperation[str, IdentifyCaller];
ProtocolDefs.SendRName[str, myName];
ProtocolDefs.SendPassword[str:str, pw:myKey, key:[0,0,0,0]];
ProtocolDefs.SendNow[str];
rc ← ProtocolDefs.ReceiveRC[str];
IF rc.code # done THEN ERROR CantFetchRegistry[name];
END;
FetchSingleEntry[name, str]; -- particularly "GV.GV" very early!
FetchType[group, name, str];
FetchType[individual, name, str];
FetchType[dead, name, str];
END;
ENDCASE => ERROR;
END;
IF str # NIL THEN str.delete[str];
END;
MakeRNameInRegistry: PROCEDURE [sname, reg, destination: BodyDefs.RName] =
BEGIN
-- sname is of the form "SN.something" or just "SN"
-- reg is of the form "NA.something" or just "NA"
-- assumes that SN and NA do not contain '.
-- constructs "SN.NA", truncating NA if needed
sep: CHARACTER = '.;
destination.length ← 0;
FOR index: CARDINAL IN [0..sname.length) WHILE sname[index] # sep
DO IF destination.length = destination.maxlength THEN ERROR;
destination[destination.length] ← sname[index];
destination.length ← destination.length + 1;
ENDLOOP;
IF destination.length = destination.maxlength THEN RETURN;
destination[destination.length] ← sep;
destination.length ← destination.length + 1;
FOR index: CARDINAL IN [0..reg.length) WHILE reg[index] # sep
DO IF destination.length = destination.maxlength THEN EXIT;
destination[destination.length] ← reg[index];
destination.length ← destination.length + 1;
ENDLOOP;
END;
FetchType: PROCEDURE[type: ProtocolDefs.RNameType, registry: BodyDefs.RName,
str: ProtocolDefs.Handle ] =
BEGIN
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[temp];
BEGIN
ENABLE UNWIND => HeapDefs.HeapAbandonWrite[writer];
typeName: BodyDefs.RName = [BodyDefs.maxRNameLength];
MakeRNameInRegistry[SELECT type FROM
group => "Groups"L, individual => "Individuals"L,
dead => "Dead"L, ENDCASE => ERROR,
registry, typeName];
IF ProtocolDefs.Enquire[str, ReadMembers, typeName].rc
# [code:done, type:group] THEN ERROR;
HeapDefs.ReceiveComponent[writer, str];
END;
BEGIN
GetEntries: PROCEDURE[ obj: HeapDefs.ObjectNumber] =
BEGIN
reader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[obj];
Work: PROCEDURE[entry: BodyDefs.RName] RETURNS[done: BOOLEAN] =
{ FetchSingleEntry[entry, str]; done ← FALSE };
HeapDefs.ReadRList[reader, Work !
UNWIND => HeapDefs.HeapEndRead[reader] ];
HeapDefs.HeapEndRead[reader];
END;
HeapDefs.HeapEndWrite[writer, GetEntries ];
END;
END;
FetchSingleEntry: PROCEDURE[entry: BodyDefs.RName,
str: ProtocolDefs.Handle] =
BEGIN
writer: HeapDefs.WriterHandle = HeapDefs.HeapStartWrite[temp];
BEGIN
ENABLE UNWIND => HeapDefs.HeapAbandonWrite[writer];
IF ProtocolDefs.Enquire[str, ReadEntry, entry].rc.code # done
THEN ERROR;
THROUGH [0.. ProtocolDefs.ReceiveCount[str] )
DO HeapDefs.ReceiveComponent[writer, str]; ENDLOOP;
END;
HeapDefs.HeapEndWrite[writer, RegServerDefs.Update];
END;
RecoverRSMailObjects: PROC[oldLimit: BodyDefs.Timestamp]
RETURNS[limit: BodyDefs.Timestamp] =
BEGIN
-- enumerate the heap looking for objects of the type created by
-- RecordDelivery. These define updates which might not have been
-- mailed before we crashed. These objects contain the time at which
-- they were written, so that we can distinguish them later from
-- objects written during this run.
name: BodyDefs.RName = [BodyDefs.maxRNameLength];
Look: PROC[object: HeapDefs.ObjectNumber] RETURNS[found: BOOLEAN] =
BEGIN
reader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[object];
then: Time.Packed;
thenStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime;
[] ← HeapDefs.HeapReadRName[reader, name];
[] ← HeapDefs.HeapReadData[reader,
[@then, SIZE[Time.Packed]] ];
ObjectDirDefs.UseObject[object];
HeapDefs.HeapEndRead[reader];
thenStamp.time ← then;
-- CompareTimestamps includes a garbage check --
IF RegistryDefs.CompareTimestamps[thenStamp, limit] = greater
THEN limit ← thenStamp;
RETURN[FALSE] --i.e. keep enumerating--
END;
limit ← oldLimit;
[] ← ObjectDirDefs.Enumerate[RSmail, Look];
END;
ActOnRSMailObjects: PROC[limit: Time.Packed] =
BEGIN
name: BodyDefs.RName = [BodyDefs.maxRNameLength];
Look: PROC[object: HeapDefs.ObjectNumber] RETURNS[found: BOOLEAN] =
BEGIN
reader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[object];
then: Time.Packed;
[] ← HeapDefs.HeapReadRName[reader, name];
[] ← HeapDefs.HeapReadData[reader,
[@then, SIZE[Time.Packed]] ];
HeapDefs.HeapEndRead[reader];
IF then <= limit
THEN RegServerDefs.MailUpdate[entry: name, stamp:, element: NIL, op: ReadEntry,
rsMailObj: object];
RETURN[FALSE] --i.e. keep enumerating--
END;
PolicyDefs.WaitOperation[regExpand];
[] ← ObjectDirDefs.Enumerate[RSmail, Look];
PolicyDefs.EndOperation[regExpand];
END;
IsThisTheFirstRServerInTheWorld: SIGNAL = CODE;
Restart: PROC =
BEGIN
-- We'd prefer to consult other reg servers, not ourselves.
-- If no other is up, we're willing to use ourself.
-- If our heap is empty and no-one else is up, we can't proceed
-- unless we're the first in the world; in that case the operator
-- must use the debugger to call "InitializeWorld".
AcceptNonLocal: PROCEDURE[addr: PupDefs.PupAddress] RETURNS[BOOLEAN] =
BEGIN RETURN[ NOT ProtocolDefs.IsLocal[addr] ] END;
info: LocateDefs.FoundServerInfo =
LocateDefs.FindRegServer[baseOfWorld, AcceptNonLocal];
myName: BodyDefs.RName;
myPassword: STRING;
myKey: BodyDefs.Password;
START RegServerDefs.Registration; -- in "none" mode --
START RegServerDefs.RegMail; -- with update propagation disbaled! --
START RegBTreeDefs.RegBTree;
START RegAccessDefs.RegAccess; -- with MS internal mail disabled! --
IF initHeap
THEN BEGIN
IF info.t = allDown -- no other servers up --
THEN BEGIN
UNTIL initializeWorldCalled
DO SIGNAL IsThisTheFirstRServerInTheWorld[] ENDLOOP;
-- operator should call "InitializeWorld" from the debugger --
RESTART RegServerDefs.RegMail; -- enable update propagation --
RESTART RegAccessDefs.RegAccess; -- enable MS internal mail --
[myName,myPassword,myKey] ← LocalNameDefs.ReadRSName[];
END
ELSE BEGIN
RESTART RegServerDefs.RegMail; -- enable update propagation --
RESTART RegAccessDefs.RegAccess; -- enable MS internal mail --
[myName,myPassword,myKey] ← LocalNameDefs.ReadRSName[];
InitializeServer[myName, myKey];
RESTART RegServerDefs.Registration; -- in "local" mode --
END;
END
ELSE BEGIN
RESTART RegServerDefs.RegMail; -- enable update propagation --
RESTART RegAccessDefs.RegAccess; -- enable MS internal mail --
IF info.t = allDown -- no other servers up --
THEN RESTART RegServerDefs.Registration; -- in "local" mode --
rsMailLimit ← InitializeFromLocalHeap[]; -- also gets local name --
[myName,myPassword,myKey] ← LocalNameDefs.ReadRSName[];
IF info.t # allDown
THEN RESTART RegServerDefs.Registration; -- in "local" mode --
END;
END;
rsMailLimit: BodyDefs.Timestamp ← BodyDefs.oldestTime;
Restart[];
STOP;
-- now Compactor has started --
Process.Detach[
FORK ActOnRSMailObjects[rsMailLimit.time]--may wait on PolicyDefs--];
RegServerDefs.ReadMail[];
RegBTreeDefs.RegPurger[];
RESTART RegServerDefs.Registration; -- in "all" mode --
END.