-- Transport Mechanism Registration Server - Access to entries.
-- [Juniper]<Grapevine>MS>RegAccess.mesa
-- Andrew Birrell 17-Jul-81 13:48:50
DIRECTORY
BodyDefs USING [maxRNameLength, RName, Timestamp],
HeapDefs USING [HeapAbandonWrite,
HeapEndRead, ObjectOffset,
objectStart, ReaderHandle,
SetReaderOffset,
WriterHandle],
LocalNameDefs USING[ ReadRSName ],
LogDefs USING[ WriteLogEntry, WriteString ],
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] };
EnableMSMail: ENTRY PROC = INLINE
{ MSMailEnabled ← TRUE };
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.WriteLogEntry[log]; LogDefs.WriteString[log];
END;
LogRemoval: INTERNAL PROC[name: BodyDefs.RName] =
BEGIN
log: STRING = [84];
String.AppendString[log, "Remove registry "L];
String.AppendString[log, name];
LogDefs.WriteLogEntry[log]; LogDefs.WriteString[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.WriteLogEntry["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.WriteLogEntry["End of removal"L];
END;
CheckReg: INTERNAL PROC[name: BodyDefs.RName]
RETURNS[RegBTreeDefs.RegState] =
BEGIN
-- determines whether "name" is in a valid and/or local registry --
RETURN[ RegBTreeDefs.TestKnownReg[name] ]
END;
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 = FORK RegChanger[];
STOP;
EnableMSMail[];
END.