-- Copyright (C) 1981, 1984 by Xerox Corporation. All rights reserved. -- RegAccess.mesa, Transport Mechanism Registration Server - Access to entries. -- HGM, 18-Nov-84 0:28:05 -- Andrew Birrell 17-Jul-81 13:48:50 -- Hankins 13-Aug-84 8:19:25 (Klamath update - remove STOPs) DIRECTORY BodyDefs USING [maxRNameLength, RName, Timestamp], HeapDefs USING [ HeapAbandonWrite, HeapEndRead, ObjectOffset, objectStart, ReaderHandle, SetReaderOffset, WriterHandle], LocalNameDefs USING [ReadRSName], LogDefs USING [ShowLine], ProtocolDefs USING [ReturnCode, RNameType], RegAccessDefs USING [NameState], RegBTreeDefs USING [ Insert, KnownRegistry, Lookup, LookupReason, RegistryObject, RegState, TestKnownReg, UpdateFailed], RegistryDefs USING [EnumerateRList], RegServerDefs USING [EnumeratedMembers, IsInList, Membership, MembershipLevel], SendDefs USING [ AddRecipient, AddToItem, Create, Destroy, Handle, StartItem, StartSend, StartSendInfo, Send], String USING [ AppendString, EquivalentStrings, EquivalentSubStrings, SubStringDescriptor]; RegAccess: MONITOR IMPORTS HeapDefs, LocalNameDefs, LogDefs, RegBTreeDefs, RegistryDefs, RegServerDefs, SendDefs, String EXPORTS RegAccessDefs = 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; -- ================ Synchronization for Registry changes ================ -- -- This layer is concerned with the integrity of the "Registry" concept, -- determining whether entries are in known registries and handling -- changes in the set of known registries. -- Under our monitor lock, Lookup determines whether this is the correct -- R-Server for the lookup. All updates also go through the monitor -- lock, and retry if we have become (or ceased to be) the correct -- server for that registry. This includes the updates which alter our -- set of registries. The BTree changes provoked by the RegPurger are -- exempt from this. Lookup: PUBLIC PROC [name: BodyDefs.RName, reason: RegBTreeDefs.LookupReason] RETURNS [info: RegAccessDefs.NameState] = BEGIN IF EndsWith[name, ".GV"L] THEN -- avoid deadlocking recursion on updates to GV names -- BEGIN treeInfo: RegBTreeDefs.RegistryObject = RegBTreeDefs.Lookup[name, reason]; info ← [ yes --Beware: BTree may not have knownReg bits yet! -- , treeInfo.type, treeInfo.stamp, treeInfo.reader]; END ELSE info ← InnerLookup[name, reason]; END; InnerLookup: PUBLIC ENTRY PROC [ name: BodyDefs.RName, reason: RegBTreeDefs.LookupReason] RETURNS [info: RegAccessDefs.NameState] = BEGIN treeInfo: RegBTreeDefs.RegistryObject = RegBTreeDefs.Lookup[name, reason]; info ← [yes, treeInfo.type, treeInfo.stamp, treeInfo.reader]; IF info.type = notFound THEN info.regState ← CheckReg[name]; END; -- This module also contains a layer for producing MS internal mail. -- For "Insert" where the old type was Individual, the old reader -- is positioned to the mailbox site list. -- To avoid problems at start-of-world, MS-mail is disabled in the early -- stages of some restarts - see RegRestart. OldReaderNeeded: ERROR = CODE; CantCreateMSInternalMail: ERROR = CODE; MSMailEnabled: BOOLEAN ← FALSE; IsMSMailEnabled: ENTRY PROC RETURNS [BOOLEAN] = INLINE {RETURN[MSMailEnabled]}; Insert: PUBLIC PROCEDURE [ name: BodyDefs.RName, type: ProtocolDefs.RNameType, stamp: POINTER TO BodyDefs.Timestamp, writer: HeapDefs.WriterHandle, oldInfo: POINTER TO RegAccessDefs.NameState] RETURNS [done: BOOLEAN] = BEGIN -- compose any message to mail servers before destroying the old reader, -- but we must commit the update before sending the mail (to avoid -- remote possibility of M-Server reading mail and re-evaluating site -- with old entry before we commit the update!). IF oldInfo.type = individual AND IsMSMailEnabled[] THEN BEGIN humanHint: STRING = "MS Internal mail for R-Name "L; myName: BodyDefs.RName; myPassword: STRING; mail: SendDefs.Handle = SendDefs.Create[]; sendInfo: SendDefs.StartSendInfo; [myName, myPassword, ] ← LocalNameDefs.ReadRSName[]; sendInfo ← SendDefs.StartSend[ handle: mail, sender: myName, senderPwd: myPassword, returnTo: "DeadLetter.MS"L, validate: FALSE]; IF sendInfo # ok THEN ERROR CantCreateMSInternalMail[]; IF oldInfo.reader = NIL THEN ERROR OldReaderNeeded[]; -- oldInfo.reader exists and is at his mailbox site list -- CopyRList[mail, oldInfo.reader]; SendDefs.StartItem[mail, reMail]; SendDefs.AddToItem[mail, DESCRIPTOR[@(name.text), name.length]]; SendDefs.StartItem[mail, Text]; -- in case it gets to DeadLetter! -- SendDefs.AddToItem[mail, DESCRIPTOR[@(humanHint.text), humanHint.length]]; SendDefs.AddToItem[mail, DESCRIPTOR[@(name.text), name.length]]; done ← ActualInsert[name, type, stamp, writer, oldInfo]; IF done THEN SendDefs.Send[mail]; SendDefs.Destroy[mail]; END ELSE done ← ActualInsert[name, type, stamp, writer, oldInfo]; END; CopyRList: PROCEDURE [message: SendDefs.Handle, reader: HeapDefs.ReaderHandle] = BEGIN Work: PROC [name: BodyDefs.RName] RETURNS [done: BOOLEAN] = BEGIN done ← FALSE; FOR i: CARDINAL DECREASING IN [0..name.length) DO IF name[i] = '. THEN EXIT; REPEAT FINISHED => GOTO notGV ENDLOOP; SendDefs.AddRecipient[message, name]; EXITS notGV => -- foreign mail server site -- NULL; END; RegistryDefs.EnumerateRList[reader, Work]; END; ActualInsert: ENTRY PROC [ name: BodyDefs.RName, type: ProtocolDefs.RNameType, stamp: POINTER TO BodyDefs.Timestamp, writer: HeapDefs.WriterHandle, oldInfo: POINTER TO RegAccessDefs.NameState] RETURNS [done: BOOLEAN] = BEGIN -- If done=FALSE at return, then oldInfo↑ has been updated -- ENABLE UNWIND => NULL; treeInfo: RegBTreeDefs.RegistryObject ← [ oldInfo.type, oldInfo.stamp, oldInfo.reader]; nameIsGVGV: BOOLEAN = String.EquivalentStrings[name, "GV.GV"L]; -- "GV.GV" is special only during the InitializeWorld restart sequence-- IF oldInfo.type = notFound AND NOT nameIsGVGV THEN BEGIN newRegState: RegBTreeDefs.RegState = CheckReg[name]; IF newRegState # oldInfo.regState THEN BEGIN IF writer # NIL THEN HeapDefs.HeapAbandonWrite[writer]; IF oldInfo.reader # NIL THEN HeapDefs.SetReaderOffset[oldInfo.reader, HeapDefs.objectStart]; oldInfo.regState ← newRegState; RETURN[FALSE] END; END; BEGIN CheckOneOfMine: INTERNAL PROC [type: ProtocolDefs.RNameType] RETURNS [BOOLEAN] = BEGIN -- determines whether "name" is a registry known to this server -- -- don't call ReadRSName[] when creating FirstRS.gv! -- RETURN[ type = group AND EndsWith[name, ".GV"L] AND (nameIsGVGV OR RegServerDefs.IsInList[ name, LocalNameDefs.ReadRSName[].name, direct, self, members].membership = yes)] END; wasMine: BOOLEAN = CheckOneOfMine[oldInfo.type]; isMine: BOOLEAN; RegBTreeDefs.Insert[ name, type, stamp, writer, @treeInfo ! RegBTreeDefs.UpdateFailed => {treeInfo ← info; GOTO failed}]; -- BTree sets knownReg bit FALSE -- done ← TRUE; isMine ← CheckOneOfMine[type]; SELECT TRUE FROM isMine => { RegBTreeDefs.KnownRegistry[name, TRUE]; IF NOT wasMine THEN LogAddition[name]}; wasMine AND NOT isMine => { RegBTreeDefs.KnownRegistry[name, FALSE]; LogRemoval[name]; StartChanger[name, FALSE] -- may unlock monitor -- }; ENDCASE => NULL; EXITS failed => BEGIN oldInfo↑ ← [ CheckReg[name], treeInfo.type, treeInfo.stamp, treeInfo.reader]; done ← FALSE; END; END; END; LogAddition: INTERNAL PROC [name: BodyDefs.RName] = BEGIN log: STRING = [84]; String.AppendString[log, "New registry "L]; String.AppendString[log, name]; LogDefs.ShowLine[log]; END; LogRemoval: INTERNAL PROC [name: BodyDefs.RName] = BEGIN log: STRING = [84]; String.AppendString[log, "Remove registry "L]; String.AppendString[log, name]; LogDefs.ShowLine[log]; END; BadRegName: ERROR = CODE; regChangerIdle: BOOLEAN ← FALSE; regChangerWanted: BOOLEAN ← FALSE; regChangerCond: CONDITION; regChangerName: BodyDefs.RName; regChangerAdd: BOOLEAN; StartChanger: INTERNAL PROC [name: BodyDefs.RName, add: BOOLEAN] = BEGIN UNTIL regChangerIdle DO WAIT regChangerCond ENDLOOP; regChangerIdle ← FALSE; -- prevent others attempting to call it -- regChangerName ← name; regChangerAdd ← add; regChangerWanted ← TRUE; -- ask it to listen -- BROADCAST regChangerCond; WHILE regChangerWanted DO WAIT regChangerCond ENDLOOP; --let it take args-- END; GetChangerArg: ENTRY PROC [regName: BodyDefs.RName] RETURNS [add: BOOLEAN] = BEGIN regChangerIdle ← TRUE; BROADCAST regChangerCond; -- open for calls -- UNTIL regChangerWanted DO WAIT regChangerCond ENDLOOP; regName.length ← 0; String.AppendString[regName, "All."L]; FOR i: CARDINAL DECREASING IN [0..regChangerName.length) DO IF regChangerName[i] = '. THEN BEGIN realLength: CARDINAL = regChangerName.length; regChangerName.length ← i; String.AppendString[regName, regChangerName]; regChangerName.length ← realLength; EXIT END; REPEAT FINISHED => ERROR BadRegName[]; ENDLOOP; add ← regChangerAdd; regChangerWanted ← FALSE; BROADCAST regChangerCond; -- free caller -- END; RegChanger: PROC = BEGIN DO regName: BodyDefs.RName = [BodyDefs.maxRNameLength]; IF GetChangerArg[regName] THEN ERROR --add registry -- ELSE DoRemoval[regName]; ENDLOOP; END; DoRemoval: PROC [regName: BodyDefs.RName] = BEGIN -- regName is "All.reg" -- LogDefs.ShowLine["Starting removal"L]; BEGIN reader: HeapDefs.ReaderHandle = RegServerDefs.EnumeratedMembers[ regName, notFound --ugh!-- ]; Action: PROC [entry: BodyDefs.RName] RETURNS [done: BOOLEAN] = BEGIN done ← FALSE; RegBTreeDefs.Insert[entry, notFound, NIL, NIL, NIL]; END; RegistryDefs.EnumerateRList[reader, Action]; HeapDefs.HeapEndRead[reader]; END; LogDefs.ShowLine["End of removal"L]; END; CheckReg: INTERNAL PROC [name: BodyDefs.RName] RETURNS [RegBTreeDefs.RegState] = -- determines whether "name" is in a valid and/or local registry {RETURN[RegBTreeDefs.TestKnownReg[name]]}; Abandon: PUBLIC PROCEDURE [ name: BodyDefs.RName, nameObj: POINTER TO RegAccessDefs.NameState] RETURNS [rc: ProtocolDefs.ReturnCode] = BEGIN IF nameObj.reader # NIL THEN HeapDefs.HeapEndRead[nameObj.reader]; rc ← IF nameObj.type = notFound AND nameObj.regState = no THEN [ WrongServer, nameObj.type] ELSE [BadRName, nameObj.type]; END; regChangerProcess: PROCESS; RegAccessInit: PUBLIC PROCEDURE = {regChangerProcess ← FORK RegChanger[]}; RegAccessMSMailEnabled: PUBLIC ENTRY PROCEDURE = {MSMailEnabled ← TRUE}; END.