-- Transport Mechanism Registration Server - restart sequence.
-- [Ibis]<Grapevine>Pilot>RegRestart.mesa
-- Pilot version - NOT source compatible with Alto Mesa version.
-- Randy Gobbel, 20-May-81 10:37:16
-- Andrew Birrell, 29-Oct-82 10:16:19
-- Ted Wobber, 2-Nov-82 11:28:09
-- Brenda Hankins 20-Aug-84 17:01:42 Klamath update
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],
LogPrivateDefs USING [tty],
ObjectDirDefs USING [Enumerate, UseObject],
PolicyDefs USING [EndOperation, WaitOperation],
Process USING [Detach],
ProtocolDefs,
PupDefs USING [PupAddress],
RegAccessDefs USING [RegAccessInit, RegAccessMSMailEnabled],
RegBTreeDefs USING [
EnumerateTree, KeepObject, Lookup, LookupReason, MarkKnown, RegBTree,
RegistryObject, RegPurger, TestKnownReg],
RegServerDefs USING [
AddMailbox, AddMember, AddOwner, ChangeConnect, CreateGroup, CreateIndividual,
IsMember, MailUpdate, ReadMail, ReadMembers, RegistrationAll,
RegistrationInit, RegistrationLocal, RegMailEnableUpdates, RegMailInit,
Update],
RegistryDefs USING [
CompareTimestamps, EnumerateRList, MakeTimestamp, ReadPrefix],
RestartDefs USING [],
Runtime USING [CallDebugger],
String USING [
AppendString, EquivalentString, EquivalentSubStrings, SubStringDescriptor],
Time USING [Append, Packed, Unpack],
TTY USING [GetChar, PutCR, PutChar, PutString];
RegRestart: PROGRAM
IMPORTS
HeapDefs, LocalNameDefs, LocateDefs, LogDefs, LogPrivateDefs, ObjectDirDefs,
PolicyDefs, Process, ProtocolDefs, RegAccessDefs, RegBTreeDefs, RegServerDefs,
RegistryDefs, Runtime, String, Time, TTY
EXPORTS EnquiryDefs --AddRegistry-- , RestartDefs =
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];
RegServerDefs.RegistrationLocal[]; -- put Registration into "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 RServer"L];
LogDefs.WriteLogEntry["Initializing Registration Server"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;
BailOut: SIGNAL = CODE;
Restart: PROCEDURE [initHeap: BOOLEAN] =
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;
RegServerDefs.RegistrationInit[]; -- start Registration in "none" mode --
RegServerDefs.RegMailInit[]; -- start RegMail with update propagation disbaled!
START RegBTreeDefs.RegBTree;
RegAccessDefs.RegAccessInit[]; -- with MS internal mail disabled! --
IF initHeap THEN
BEGIN
IF info.t = allDown -- no other servers up --
THEN
BEGIN
wish: CHARACTER;
DO
TTY.PutString[
LogPrivateDefs.tty,
"No other RServers were found. Type 'Y' if this the first RServer in The World (you better be correct...) : "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
SELECT wish FROM
'N, 'n => SIGNAL BailOut[];
-- it could be that others are just down, then what? reboot?
'Y, 'y =>
BEGIN
TTY.PutString[
LogPrivateDefs.tty, "Do you know what you're doing? (Y or N): "L];
wish ← TTY.GetChar[LogPrivateDefs.tty];
TTY.PutChar[LogPrivateDefs.tty, wish];
TTY.PutCR[LogPrivateDefs.tty];
SELECT wish FROM
'N, 'n => SIGNAL BailOut[];
'Y, 'y => EXIT; -- go on to initialize the world
ENDCASE => LOOP;
END;
ENDCASE => LOOP;
ENDLOOP;
InitializeWorld[];
RegServerDefs.RegMailEnableUpdates[]; -- enable update propagation --
RegAccessDefs.RegAccessMSMailEnabled; -- enable MS internal mail --
[myName, myPassword, myKey] ← LocalNameDefs.ReadRSName[];
END
ELSE
BEGIN
RegServerDefs.RegMailEnableUpdates[]; -- enable update propagation --
RegAccessDefs.RegAccessMSMailEnabled[]; -- enable MS internal mail --
[myName, myPassword, myKey] ← LocalNameDefs.ReadRSName[];
InitializeServer[myName, myKey];
RegServerDefs.RegistrationLocal[]; -- put Reg. into "local" mode
END;
END
ELSE
BEGIN
RegServerDefs.RegMailEnableUpdates[]; -- enable update propagation --
RegAccessDefs.RegAccessMSMailEnabled[]; -- enable MS internal mail --
IF info.t = allDown THEN -- no other servers up --
RegServerDefs.RegistrationLocal[]; -- put Reg into "local" mode --
rsMailLimit ← InitializeFromLocalHeap[]; -- also gets local name --
[myName, myPassword, myKey] ← LocalNameDefs.ReadRSName[];
IF info.t # allDown THEN RegServerDefs.RegistrationLocal[]; -- put Reg into "local" mode --
END;
END;
rsMailLimit: BodyDefs.Timestamp;
RegRestartInit1: PUBLIC PROCEDURE [initHeap: BOOLEAN] = {
rsMailLimit ← BodyDefs.oldestTime; Restart[initHeap]};
RegRestartInit2: PUBLIC PROCEDURE =
BEGIN -- now Compactor has started --
Process.Detach[
FORK ActOnRSMailObjects[[rsMailLimit.time]] --may wait on PolicyDefs-- ];
RegServerDefs.ReadMail[];
RegBTreeDefs.RegPurger[];
RegServerDefs.RegistrationAll[]; -- put Registration into "all" mode --
END;
END.
13-Aug-84 8:28:21 making init query interactive - BLH
13-Aug-84 8:28:43 reworking STOPs and RESTARTs - blh