-- 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, "Discarded "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.