-- Transport Mechanism Registration Server - Internal Mail. -- [Indigo]<Grapevine>MS>RegMail.mesa -- Randy Gobbel, 19-May-81 12:26:40 -- Andrew Birrell, 18-Nov-82 10:33:12 -- Mike Schroeder 8-Feb-83 9:43:22 DIRECTORY BodyDefs USING[ ItemHeader, ItemLength, ItemType, maxRNameLength, oldestTime, Password, RName, RNameSize, Timestamp], EnquiryDefs USING[ ], HeapDefs USING[ HeapEndWrite, HeapEndRead, HeapReadData, HeapStartWrite, HeapWriteData, ObjectNumber, ReaderHandle, WriterHandle], LocalNameDefs USING[ ReadRSName ], LogDefs USING[ WriteLogEntry ], ObjectDirDefs USING[ FreeObject, noObject ], PolicyDefs USING[ CheckOperation, EndOperation, WaitOperation ], Process USING[ Detach, DisableTimeout ], ProtocolDefs USING[ AppendTimestamp, ReturnCode, RSOperation ], RegBTreeDefs USING[ Lookup, LookupReason, RegistryObject ], RegServerDefs USING[ IsMember, ReadRegistryMembers, Update, UpdateSublist ], RegistryDefs USING[ EnumerateRList], RetrieveDefs USING[ AccessProcs, Failed, Handle, Create, MailboxState, NewUser, NextServer, ServerName, ServerState, WaitForMail ], SendDefs USING[ Abort, AddRecipient, AddToItem, Create, Destroy, Handle, StartItem, StartSend, Send ], String USING[ AppendString, EquivalentString], VMDefs USING[ AllocatePage, Page, pageSize, Release]; RegMail: MONITOR IMPORTS BodyDefs, HeapDefs, LocalNameDefs, LogDefs, ObjectDirDefs, PolicyDefs, Process, RegBTreeDefs, RegistryDefs, RegServerDefs, RetrieveDefs, SendDefs, String, VMDefs EXPORTS EnquiryDefs--LoginRSMail--, RegServerDefs = BEGIN OPEN ProtocolDefs; -- the update mail is generated in a separate process to allow return to -- client sooner, and to avoid deadlock if we generate an update while -- determining our R-Server name. updateEntry: BodyDefs.RName = [BodyDefs.maxRNameLength]; updateStamp: BodyDefs.Timestamp; updateElement: BodyDefs.RName = [BodyDefs.maxRNameLength]; updateOp: ProtocolDefs.RSOperation; updateRSMailObj: HeapDefs.ObjectNumber; updatePending: BOOLEAN ← FALSE; updateCond: CONDITION; updateEnabled: BOOLEAN ← FALSE; -- set TRUE early in restart -- NoRegistryForName: ERROR = CODE; FailedToSendUpdate: ERROR = CODE; EnableUpdate: ENTRY PROC = INLINE { updateEnabled ← TRUE }; MailUpdate: PUBLIC ENTRY PROCEDURE [entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation, rsMailObj: HeapDefs.ObjectNumber] = BEGIN IF updateEnabled THEN BEGIN WHILE updatePending DO WAIT updateCond ENDLOOP; updateEntry.length ← 0; String.AppendString[updateEntry, entry]; updateStamp ← stamp; updateElement.length ← 0; IF element # NIL THEN String.AppendString[updateElement, element]; updateOp ← op; updateRSMailObj ← rsMailObj; updatePending ← TRUE; NOTIFY updateCond; END ELSE IF rsMailObj # ObjectDirDefs.noObject THEN ObjectDirDefs.FreeObject[rsMailObj]; END; SendRSMail: ENTRY PROC = BEGIN DO UNTIL updatePending DO WAIT updateCond ENDLOOP; SendUpdate[updateEntry, updateStamp, updateElement, updateOp]; IF updateRSMailObj # ObjectDirDefs.noObject THEN ObjectDirDefs.FreeObject[updateRSMailObj]; updatePending ← FALSE; NOTIFY updateCond; ENDLOOP; END; briefUpdate: BodyDefs.ItemType = LOOPHOLE[2001B]; briefUpdateAllowed: BOOLEAN ← TRUE; SendUpdate: INTERNAL PROC[entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation] = BEGIN -- mail the object to other registration servers for entry's registry. regobj: RegBTreeDefs.RegistryObject = RegBTreeDefs.Lookup[entry, readAny]; myName: BodyDefs.RName; myPassword: STRING; [myName, myPassword,] ← LocalNameDefs.ReadRSName[]; IF regobj.type # notFound THEN BEGIN message: SendDefs.Handle = SendDefs.Create[]; itsRegReader: HeapDefs.ReaderHandle = LookupRegistry[entry]; IF SendDefs.StartSend[handle: message, senderPwd: myPassword, sender: myName, returnTo: "DeadLetter.MS"L, validate: FALSE] # ok THEN ERROR FailedToSendUpdate[]; IF CopyRList[message, myName, itsRegReader] > 0 THEN BEGIN humanHint: STRING = "RS internal mail for R-Name "L; SELECT op FROM AddMember, DeleteMember => IF briefUpdateAllowed THEN BriefUpdate[message, entry, stamp, element, op] ELSE FullUpdate[message, regobj.reader]; ENDCASE => FullUpdate[message, regobj.reader]; HeapDefs.HeapEndRead[regobj.reader]; -- in case it gets to DeadLetter! -- SendDefs.StartItem[message, Text]; SendDefs.AddToItem[message, DESCRIPTOR[ @(humanHint.text), humanHint.length ] ]; SendDefs.AddToItem[message, DESCRIPTOR[ @(entry.text), entry.length]]; SendDefs.Send[message]; END ELSE BEGIN --no recipients-- SendDefs.Abort[message]; HeapDefs.HeapEndRead[regobj.reader]; END; SendDefs.Destroy[message]; END; END; LookupRegistry: PROCEDURE[name: BodyDefs.RName] RETURNS[ reader: HeapDefs.ReaderHandle ] = BEGIN oldTimePtr: BodyDefs.Timestamp ← BodyDefs.oldestTime; --ugh!-- rc: ProtocolDefs.ReturnCode; [reader,rc] ← RegServerDefs.ReadRegistryMembers[name, @oldTimePtr]; IF rc.code # done THEN ERROR NoRegistryForName[]; END; CopyRList: PROCEDURE[ message: SendDefs.Handle, me: BodyDefs.RName, reader: HeapDefs.ReaderHandle] RETURNS[ recipients: CARDINAL ] = BEGIN Work: PROC[name: BodyDefs.RName] RETURNS[done:BOOLEAN] = BEGIN done ← FALSE; IF NOT String.EquivalentString[me, name] THEN BEGIN SendDefs.AddRecipient[message, name]; recipients ← recipients + 1; END; END; recipients ← 0; RegistryDefs.EnumerateRList[reader, Work]; HeapDefs.HeapEndRead[reader]; END; FullUpdate: PROCEDURE[message: SendDefs.Handle, reader: HeapDefs.ReaderHandle] = BEGIN buffer: VMDefs.Page = VMDefs.AllocatePage[]; ended: BOOLEAN; used: CARDINAL; SendDefs.StartItem[message, updateItem ]; DO [ended,used] ← HeapDefs.HeapReadData[reader, [buffer, VMDefs.pageSize]]; SendDefs.AddToItem[message, DESCRIPTOR[LOOPHOLE[buffer,POINTER], 2*used] ]; IF ended THEN EXIT; ENDLOOP; VMDefs.Release[buffer]; END; BriefUpdate: PROCEDURE[message: SendDefs.Handle, entry: BodyDefs.RName, stamp: BodyDefs.Timestamp, element: BodyDefs.RName, op: ProtocolDefs.RSOperation] = BEGIN SendDefs.StartItem[message, briefUpdate ]; SendDefs.AddToItem[message, DESCRIPTOR[LOOPHOLE[@op,POINTER], 2*SIZE[ProtocolDefs.RSOperation]]]; SendDefs.AddToItem[message, DESCRIPTOR[LOOPHOLE[entry,POINTER], 2*BodyDefs.RNameSize[entry]]]; SendDefs.AddToItem[message, DESCRIPTOR[LOOPHOLE[element,POINTER], 2*BodyDefs.RNameSize[element]]]; SendDefs.AddToItem[message, DESCRIPTOR[LOOPHOLE[@stamp,POINTER], 2*SIZE[BodyDefs.Timestamp]]]; END; -- RS Mail reading -- handle: RetrieveDefs.Handle = RetrieveDefs.Create[pollingInterval: 30]; readerActive: BOOLEAN ← FALSE; readerInactive: CONDITION ← [timeout:0]; LoginRSMail: PUBLIC ENTRY PROC = BEGIN myName: BodyDefs.RName; myPassword: STRING; WHILE readerActive DO WAIT readerInactive ENDLOOP; [myName, myPassword,] ← LocalNameDefs.ReadRSName[]; RetrieveDefs.NewUser[handle: handle, user: myName, password: myPassword]; DO sName: BodyDefs.RName = [BodyDefs.maxRNameLength]; noMore: BOOLEAN; [noMore,,] ← RetrieveDefs.NextServer[handle]; IF noMore THEN EXIT; LogInboxSite["RS inbox-site: "L]; ENDLOOP; END; LogInboxSite: INTERNAL PROC[s: STRING] = BEGIN log: STRING = [64]; server: STRING = [64]; RetrieveDefs.ServerName[handle, server]; String.AppendString[log, s]; server.length ← MIN [ server.length, log.maxlength - log.length]; String.AppendString[log, server]; LogDefs.WriteLogEntry[log]; END; LockReader: ENTRY PROC = INLINE { readerActive ← TRUE }; UnlockReader: ENTRY PROC = INLINE { readerActive ← FALSE; NOTIFY readerInactive }; MyNameIsBad: ERROR = CODE; MyPwdIsBad: ERROR = CODE; ReadMail: PUBLIC PROCEDURE = BEGIN -- Reads outstanding mail before returning -- LoginRSMail[]; SELECT RetrieveDefs.MailboxState[handle] FROM badName => ERROR MyNameIsBad[]; badPwd => ERROR MyPwdIsBad[]; notEmpty => IF PolicyDefs.CheckOperation[RSReadMail] THEN { RegMailReader[]; PolicyDefs.EndOperation[RSReadMail] }; ENDCASE => NULL; Process.Detach[FORK RegMailWatcher[]]; END; RegMailWatcher: PROC = BEGIN DO RetrieveDefs.WaitForMail[handle]; PolicyDefs.WaitOperation[RSReadMail]; RegMailReader[]; PolicyDefs.EndOperation[RSReadMail]; ENDLOOP; END; RegMailReader: PROC = BEGIN LockReader[]; DO noMore: BOOLEAN; state: RetrieveDefs.ServerState; procs: RetrieveDefs.AccessProcs; gv: GV RetrieveDefs.AccessProcs; [noMore, state, procs] ← RetrieveDefs.NextServer[handle]; IF noMore THEN EXIT; IF state # notEmpty THEN LOOP; WITH gvProcs:procs SELECT FROM GV => gv←gvProcs; ENDCASE => LOOP; BEGIN ENABLE RetrieveDefs.Failed => CONTINUE; pleaseFlush: BOOLEAN ← TRUE; DO sender: BodyDefs.RName = [BodyDefs.maxRNameLength]; time: BodyDefs.TimeStamp; msgExists, archived, deleted: BOOLEAN; pleaseDelete: BOOLEAN ← TRUE; [msgExists, archived, deleted] ← gv.nextMessage[handle]; IF deleted THEN LOOP; IF NOT msgExists THEN EXIT; gv.startMessage[handle:handle, sender:sender, postmark: @time]; IF CheckAndLog[sender, time] THEN DO header: BodyDefs.ItemHeader = gv.nextItem[handle]; SELECT header.type FROM LastItem => EXIT; updateItem => BEGIN AcceptUpdate[handle, @gv]; EXIT END; briefUpdate => BEGIN IF NOT AcceptBriefUpdate[handle, header.length, @gv] THEN pleaseDelete ← FALSE; EXIT END; ENDCASE => NULL; ENDLOOP--each item--; IF pleaseDelete THEN gv.deleteMessage[handle] ELSE pleaseFlush ← FALSE; ENDLOOP--each message--; IF pleaseFlush THEN gv.accept[handle]; END; ENDLOOP--each server--; UnlockReader[]; END; CheckAndLog: PROC[sender: BodyDefs.RName, time: BodyDefs.Timestamp] RETURNS [good: BOOLEAN] = BEGIN log: STRING = [128]; good ← TRUE; String.AppendString[log, "RS reading "L]; ProtocolDefs.AppendTimestamp[log, time]; String.AppendString[log, " from "L]; String.AppendString[log, sender]; IF RegServerDefs.IsMember["*.gv"L, sender, direct].membership # yes THEN {String.AppendString[log, ": bad sender"L]; good ← FALSE}; LogDefs.WriteLogEntry[log]; END; GetMsgItem: PROCEDURE[handle: RetrieveDefs.Handle, gv: POINTER TO GV RetrieveDefs.AccessProcs] RETURNS[ writer: HeapDefs.WriterHandle ] = BEGIN buffer: VMDefs.Page = VMDefs.AllocatePage[]; length: CARDINAL; writer ← HeapDefs.HeapStartWrite[temp]; WHILE (length ← gv.nextBlock[handle, DESCRIPTOR[buffer, 2*VMDefs.pageSize] ]) > 0 DO HeapDefs.HeapWriteData[writer, [buffer, (1+length)/2] ]; ENDLOOP; VMDefs.Release[buffer]; END; MangledMessage: ERROR = CODE; AcceptUpdate: PROCEDURE[handle: RetrieveDefs.Handle, gv: POINTER TO GV RetrieveDefs.AccessProcs] = BEGIN updateWriter: HeapDefs.WriterHandle = GetMsgItem[handle, gv]; AcceptRestOfMessage[handle, gv]; HeapDefs.HeapEndWrite[updateWriter, RegServerDefs.Update]; END; --AcceptUpdate-- AcceptBriefUpdate: PROCEDURE[handle: RetrieveDefs.Handle, itemLength: BodyDefs.ItemLength, gv: POINTER TO GV RetrieveDefs.AccessProcs] RETURNS[ ok: BOOLEAN ] = BEGIN strHeader: CARDINAL = 2*SIZE[StringBody[0]]; op: ProtocolDefs.RSOperation; entry: BodyDefs.RName = [BodyDefs.maxRNameLength]; element: BodyDefs.RName = [BodyDefs.maxRNameLength]; stamp: BodyDefs.Timestamp; rc: ProtocolDefs.ReturnCode; length: CARDINAL; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[@op,POINTER], 2*SIZE[ProtocolDefs.RSOperation]]]; itemLength ← itemLength - length; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[entry,POINTER], strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[@entry.text,POINTER], 2*BodyDefs.RNameSize[entry]-strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[element,POINTER], strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[@element.text,POINTER],2*BodyDefs.RNameSize[element]-strHeader]]; itemLength ← itemLength - length; length ← gv.nextBlock[handle, DESCRIPTOR[LOOPHOLE[@stamp,POINTER], 2*SIZE[BodyDefs.Timestamp]]]; itemLength ← itemLength - length; IF itemLength # 0 OR length # 2*SIZE[BodyDefs.Timestamp] THEN ERROR MangledMessage[]; AcceptRestOfMessage[handle, gv]; rc ← RegServerDefs.UpdateSublist[entry, element, op, stamp]; RETURN[ rc.type # notFound ] END; AcceptRestOfMessage: PROCEDURE[handle: RetrieveDefs.Handle, gv: POINTER TO GV RetrieveDefs.AccessProcs] = BEGIN DO header: BodyDefs.ItemHeader = gv.nextItem[handle]; SELECT header.type FROM LastItem => EXIT; ENDCASE => NULL; ENDLOOP; END; --AcceptRestOfMessage-- Process.DisableTimeout[@updateCond]; Process.Detach[ FORK SendRSMail[] ]; STOP; EnableUpdate[]; END.