DIRECTORY Arpa USING [Address, MyAddress, nullAddress], ArpaUDP USING [nullPort, Port], Ascii USING [CR, FF, LF, SP, TAB], Basics USING [HFromCard16, IsBound, LowHalf], BasicTime USING [earliestGMT, GMT, Now, Period, TimeNotKnown, Update], Convert USING [RopeFromArpaAddress], Process USING [Detach, PauseMsec], RefText USING [Append, New, ObtainScratch, ReleaseScratch], Rope USING [FromRefText, Length, ROPE, Substr, ToRefText], SunPMap USING [udpPort], SunRPC USING [Destroy, Error, Handle], SunRPCOnUDP USING [Create, SetRemote], SunRPCAuth USING [Conversation, Error, Initiate, Terminate], SunYP USING [EachMapNameProc, ResponseKeyVal, ResponseVal, Status], SunYPAgent USING [TextSeq, TextSeqObject], SunYPAgentPrivate USING [Object, ServerObject], SunYPClient USING [First, Maplist, Match, Next, Null], SunYPFind USING [Error, GetDefaultDomain, GetServerAddressAndPortForDomain], SymTab USING [Create, Fetch, Ref, Store, Update, UpdateAction, Val], ThisMachineRegistry USING [ThisMachineProcsRec, ThisMachineRef, RegisterThisMachineProcs] ; SunYPAgentImpl: CEDAR MONITOR LOCKS lock USING lock: Lock IMPORTS Arpa, Basics, BasicTime, Convert, Process, RefText, Rope, SunRPC, SunRPCOnUDP, SunRPCAuth, SunYPClient, SunYPFind, SymTab, ThisMachineRegistry EXPORTS SunYPAgent ~ { msecBetweenSweeps: CARD ¬ 11111; initialServerTTL: CARDINAL ¬ 300; initialCachedValueTTL: CARDINAL ¬ 300; maxCachedValues: CARDINAL ¬ 31; timeoutBase: CARD ¬ 500; timeoutMsecPerHop: CARD ¬ 250; pauseMsecPerHop: CARD ¬ 100; maxTriesForSpecifiedServer: CARDINAL ¬ 8; pauseMsecBetweenRetriesForSpecifiedServer: CARD ¬ 1000; pMapPort: ArpaUDP.Port ¬ Basics.HFromCard16[Basics.LowHalf[SunPMap.udpPort]]; maxArgLength: CARDINAL ¬ 100; hostsMapName: ROPE ¬ "hosts.byaddr"; hostNamePos: CARDINAL = 1; ROPE: TYPE ~ Rope.ROPE; Lock: TYPE ~ REF LockObject; LockObject: TYPE ~ MONITORED RECORD []; Error: PUBLIC ERROR [code: ATOM] ~ CODE; Server: TYPE ~ REF ServerObject; ServerObject: TYPE ~ SunYPAgentPrivate.ServerObject; serverTab: SymTab.Ref ¬ SymTab.Create[]; serverLock: Lock ~ NEW[LockObject]; serverList: Server ¬ NIL; nullServerLock: Lock ~ NEW[LockObject]; nullServerList: Server ¬ NIL; GetServerByAddressAndPort: ENTRY PROC [address: Arpa.Address, port: ArpaUDP.Port, lock: Lock ¬ serverLock] RETURNS [server: Server] ~ { FOR server ¬ serverList, server.next WHILE (server # NIL) AND ((server.address # address) OR (server.port # port)) DO NULL ENDLOOP; IF server = NIL THEN { server ¬ NEW[ServerObject ¬ [serverList, address, port, initialServerTTL, FALSE]]; serverList ¬ server; }; }; SweepServers: ENTRY PROC [seconds: CARD, lock: Lock ¬ serverLock] ~ { FOR server: Server ¬ serverList, server.next WHILE server # NIL DO server.ttl ¬ (IF server.ttl > seconds THEN server.ttl - seconds ELSE 0); ENDLOOP; }; IsNullServer: PROC [server: Server] RETURNS [BOOL] ~ INLINE { RETURN [server.address = Arpa.nullAddress] }; GetNullServer: ENTRY PROC [lock: Lock ¬ nullServerLock] RETURNS [server: Server] ~ { server ¬ NEW[ServerObject ¬ [nullServerList, Arpa.nullAddress, ArpaUDP.nullPort, initialServerTTL, FALSE]]; nullServerList ¬ server; }; SweepNullServers: ENTRY PROC [seconds: CARD, lock: Lock ¬ nullServerLock] ~ { prev, server: Server; prev ¬ NIL; server ¬ nullServerList; WHILE server # NIL DO IF server.ttl > seconds THEN { server.ttl ¬ server.ttl - seconds; prev ¬ server; } ELSE { server.ttl ¬ 0; IF prev = NIL THEN nullServerList ¬ server.next ELSE prev.next ¬ server.next; }; server ¬ server.next; ENDLOOP; }; GetServerForDomain: PROC [domain: ROPE] RETURNS [server: Server] ~ { server ¬ NARROW[SymTab.Fetch[serverTab, domain].val]; SELECT TRUE FROM (server = NIL) => NULL; (IsNullServer[server]) => IF server.ttl = 0 THEN server ¬ NIL; (server.down) => server ¬ NIL; (server.ttl = 0) AND (NOT ServerResponding[server]) => server ¬ NIL; ENDCASE; IF server # NIL THEN RETURN; -- IF server = NIL THEN -- { address: Arpa.Address ¬ Arpa.nullAddress; port: ArpaUDP.Port ¬ ArpaUDP.nullPort; [address, port] ¬ SunYPFind.GetServerAddressAndPortForDomain[domain ! SunYPFind.Error => CONTINUE]; IF address # Arpa.nullAddress THEN server ¬ GetServerByAddressAndPort[address, port]; }; IF server = NIL THEN { server ¬ GetNullServer[]; }; [] ¬ SymTab.Store[serverTab, domain, server]; }; GetServerByAddress: PROC [address: Arpa.Address, domain: ROPE] RETURNS [server: Server] ~ { errorCode: ATOM ¬ NIL; port: ArpaUDP.Port; [, port] ¬ SunYPFind.GetServerAddressAndPortForDomain[domain, address ! SunYPFind.Error => { errorCode ¬ code; CONTINUE } ]; IF errorCode = NIL THEN { server ¬ GetServerByAddressAndPort[address, port]; server.down ¬ FALSE; server.ttl ¬ initialServerTTL; [] ¬ SymTab.Store[serverTab, domain, server]; } ELSE { ERROR Error[errorCode]; }; }; ServerResponding: PROC [server: Server] RETURNS [responding: BOOL ¬ FALSE] ~ { h: SunRPC.Handle; c: SunRPCAuth.Conversation; IF NOT IsNullServer[server] THEN { ENABLE SunRPC.Error, SunRPCAuth.Error => CONTINUE; h ¬ SunRPCOnUDP.Create[remoteAddress~server.address, remotePort~server.port]; c ¬ SunRPCAuth.Initiate[]; server.ttl ¬ initialServerTTL; SunYPClient.Null[h, c]; responding ¬ TRUE; }; server.down ¬ NOT responding; IF h # NIL THEN SunRPC.Destroy[h]; h ¬ NIL; IF c # NIL THEN SunRPCAuth.Terminate[c]; c ¬ NIL; }; Handle: TYPE ~ REF Object; Object: PUBLIC TYPE ~ SunYPAgentPrivate.Object; ObtainHandle: PUBLIC PROC [domainName: ROPE, conversation: SunRPCAuth.Conversation, serverAddress: Arpa.Address] RETURNS [h: Handle] ~ { server: Server ¬ NIL; rpcHandle: SunRPC.Handle; serverSpecified: BOOL ¬ (serverAddress # Arpa.nullAddress); IF domainName = NIL THEN domainName ¬ SunYPFind.GetDefaultDomain[]; IF serverSpecified THEN { FOR try: CARDINAL ¬ 0, try.SUCC DO server ¬ GetServerByAddress[serverAddress, domainName ! Error => IF try >= maxTriesForSpecifiedServer THEN REJECT ELSE CONTINUE]; IF server # NIL THEN EXIT; Process.PauseMsec[pauseMsecBetweenRetriesForSpecifiedServer]; ENDLOOP; } ELSE { server ¬ GetServerForDomain[domainName]; IF IsNullServer[server] THEN ERROR Error[$domainNotFound]; }; IF conversation = NIL THEN conversation ¬ SunRPCAuth.Initiate[]; rpcHandle ¬ SunRPCOnUDP.Create[remoteAddress~server.address, remotePort~server.port]; h ¬ NEW[Object ¬ [domain~domainName, server~server, rpcHandle~rpcHandle, conversation~conversation, serverSpecified~serverSpecified]]; }; RefreshHandle: PUBLIC PROC [h: Handle, conversation: SunRPCAuth.Conversation, serverAddress: Arpa.Address] ~ { server: Server; IF serverAddress = Arpa.nullAddress THEN { server ¬ GetServerForDomain[h.domain]; IF IsNullServer[server] THEN ERROR Error[$domainNotFound]; } ELSE { server ¬ GetServerByAddress[serverAddress, h.domain]; }; SunRPCAuth.Terminate[h.conversation]; h.conversation ¬ IF conversation # NIL THEN conversation ELSE SunRPCAuth.Initiate[]; IF server # h.server THEN { h.server ¬ server; h.rpcHandle ¬ SunRPCOnUDP.SetRemote[h.rpcHandle, server.address, server.port]; }; }; ReleaseHandle: PUBLIC PROC [h: Handle] ~ { IF h.rpcHandle # NIL THEN { SunRPC.Destroy[h.rpcHandle]; h.rpcHandle ¬ NIL }; h.server ¬ NIL; IF h.conversation # NIL THEN { SunRPCAuth.Terminate[h.conversation]; h.conversation ¬ NIL }; }; FixupForDownServer: PROC [h: Handle] RETURNS [fixed: BOOL ¬ FALSE] ~ { ENABLE Error => CONTINUE; oldServer: Server; IF h.serverSpecified THEN RETURN; oldServer ¬ h.server; h.server ¬ GetServerForDomain[h.domain]; IF IsNullServer[h.server] THEN RETURN; IF h.server.address = oldServer.address THEN RETURN; IF h.server.down THEN RETURN; fixed ¬ TRUE; }; CachedValue: TYPE ~ REF CachedValueObject; CachedValueObject: TYPE ~ RECORD [ next, prev: CachedValue, ttl: CARDINAL ¬ initialCachedValueTTL, busy: CARDINAL ¬ 0, value: REF TEXT, key: ROPE, table: SymTab.Ref ]; cachedValueLock: Lock ~ NEW[LockObject]; cachedValueHead, cachedValueTail: CachedValue; numCachedValues: CARD ¬ 0; cachedValueAvailable: CONDITION; tabByDomain: SymTab.Ref ~ SymTab.Create[]; RemoveCachedValueFromTable: PROC [cV: CachedValue] ~ { value: SymTab.Val ~ cV; DeleteValue: SymTab.UpdateAction -- [found, val] RETURNS [op, new] -- ~ { IF found AND val = value THEN RETURN [delete, NIL] ELSE RETURN [none, NIL]; }; IF (cV.table = NIL) OR (cV.key = NIL) THEN ERROR; SymTab.Update[cV.table, cV.key, DeleteValue]; }; RemoveCachedValueFromList: INTERNAL PROC [cV: CachedValue] ~ { IF cV = cachedValueTail THEN cachedValueTail ¬ cV.prev ELSE cV.next.prev ¬ cV.prev; IF cV = cachedValueHead THEN cachedValueHead ¬ cV.next ELSE cV.prev.next ¬ cV.next; }; LookupCachedValue: ENTRY PROC [domainName, mapName, key: ROPE, lock: Lock ¬ cachedValueLock] RETURNS [cV: CachedValue] ~ { tabByMap, tabByKey: SymTab.Ref; GetTabByMap: SymTab.UpdateAction -- [found, val] RETURNS [op, new] -- ~ { IF found THEN { tabByMap ¬ NARROW[val]; RETURN [none, NIL] } ELSE { tabByMap ¬ SymTab.Create[]; RETURN [store, tabByMap] }; }; GetTabByKey: SymTab.UpdateAction -- [found, val] RETURNS [op, new] -- ~ { IF found THEN { tabByKey ¬ NARROW[val]; RETURN [none, NIL] } ELSE { tabByKey ¬ SymTab.Create[]; RETURN [store, tabByKey] }; }; SymTab.Update[tabByDomain, domainName, GetTabByMap]; SymTab.Update[tabByMap, mapName, GetTabByKey]; cV ¬ NARROW[SymTab.Fetch[tabByKey, key].val]; SELECT TRUE FROM (cV = NIL) => { DO SELECT TRUE FROM (numCachedValues < maxCachedValues) => { cV ¬ NEW[CachedValueObject]; numCachedValues ¬ numCachedValues + 1; EXIT; }; (cachedValueHead = NIL) => { WAIT cachedValueAvailable; LOOP; }; ENDCASE => { cV ¬ cachedValueTail; RemoveCachedValueFromTable[cV]; RemoveCachedValueFromList[cV]; EXIT; }; ENDLOOP; cV.ttl ¬ initialCachedValueTTL; cV.key ¬ key; cV.value ¬ NIL; cV.table ¬ tabByKey; }; (cV.busy = 0) => { RemoveCachedValueFromList[cV]; }; ENDCASE; cV.busy ¬ cV.busy + 1; }; CacheValue: ENTRY PROC [cV: CachedValue, newValue: REF TEXT ¬ NIL, lock: Lock ¬ cachedValueLock] ~ { IF newValue # NIL THEN { IF cV.busy # 1 THEN ERROR; cV.value ¬ newValue; [] ¬ SymTab.Store[cV.table, cV.key, cV]; }; IF (cV.busy ¬ cV.busy - 1) = 0 THEN { IF cachedValueHead = NIL THEN { cV.next ¬ cV.prev ¬ NIL; cachedValueHead ¬ cachedValueTail ¬ cV; NOTIFY cachedValueAvailable; } ELSE { cV.next ¬ cachedValueHead; cV.prev ¬ NIL; cachedValueHead.prev ¬ cV; cachedValueHead ¬ cV; }; }; }; FreeCachedValue: INTERNAL PROC [cV: CachedValue, lock: Lock] ~ { cV.key ¬ NIL; cV.value ¬ NIL; cV.table ¬ NIL; cV.next ¬ cV.prev ¬ NIL; numCachedValues ¬ numCachedValues - 1; NOTIFY cachedValueAvailable; }; ReleaseCacheEntry: ENTRY PROC [cV: CachedValue, lock: Lock ¬ cachedValueLock] ~ { IF cV.busy # 1 THEN ERROR; FreeCachedValue[cV, lock]; }; SweepCachedValues: ENTRY PROC [seconds: CARD, lock: Lock ¬ cachedValueLock] ~ { p, next: CachedValue; FOR p ¬ cachedValueHead, next WHILE p # NIL DO next ¬ p.next; IF p.ttl > seconds THEN { p.ttl ¬ p.ttl - seconds; } ELSE { IF p.busy # 0 THEN ERROR; RemoveCachedValueFromTable[p]; RemoveCachedValueFromList[p]; FreeCachedValue[p, lock]; }; ENDLOOP; }; MapErrorCode: PROC [code: ATOM, h: Handle] RETURNS [mappedCode: ATOM] ~ { h.server.down ¬ TRUE; h.server.ttl ¬ initialServerTTL; mappedCode ¬ code; }; MapErrorStatus: PROC [status: SunYP.Status, h: Handle] RETURNS [mappedCode: ATOM] ~ { h.server.down ¬ FALSE; h.server.ttl ¬ initialServerTTL; mappedCode ¬ (SELECT status FROM true => NIL, nomore => $noMoreEntries, nomap => $mapNotFound, nodomain => $domainNotFound, nokey => $keyNotFound, ENDCASE => $protocol); }; Match: PUBLIC PROC [h: Handle, map: ROPE, key: ROPE] RETURNS [val: REF TEXT] ~ { cV: CachedValue; errorCode: ATOM; response: SunYP.ResponseVal; cV ¬ LookupCachedValue[h.domain, map, key]; IF cV.value # NIL THEN { val ¬ cV.value; CacheValue[cV]; RETURN; }; { ENABLE SunRPC.Error => { errorCode ¬ MapErrorCode[code, h]; IF FixupForDownServer[h] THEN RETRY ELSE CONTINUE; }; response ¬ SunYPClient.Match[h.rpcHandle, h.conversation, [domain~h.domain, map~map, key~Rope.ToRefText[key]]]; errorCode ¬ MapErrorStatus[response.status, h]; }; IF errorCode = NIL THEN { val ¬ response.val; CacheValue[cV, val]; RETURN; } ELSE { ReleaseCacheEntry[cV]; ERROR Error[errorCode]; }; }; First: PUBLIC PROC [h: Handle, map: ROPE] RETURNS [key: ROPE, val: REF TEXT] ~ { response: SunYP.ResponseKeyVal; errorCode: ATOM ¬ NIL; { ENABLE SunRPC.Error => { errorCode ¬ MapErrorCode[code, h]; IF FixupForDownServer[h] THEN RETRY ELSE CONTINUE; }; response ¬ SunYPClient.First[h.rpcHandle, h.conversation, [domain~h.domain, map~map]]; errorCode ¬ MapErrorStatus[response.status, h]; }; IF errorCode # NIL THEN ERROR Error[errorCode]; RETURN [Rope.FromRefText[response.key], response.val]; }; Next: PUBLIC PROC [h: Handle, map: ROPE, keyBefore: ROPE] RETURNS [key: ROPE, val: REF TEXT] ~ { response: SunYP.ResponseKeyVal; errorCode: ATOM ¬ NIL; { ENABLE SunRPC.Error => { errorCode ¬ MapErrorCode[code, h]; IF FixupForDownServer[h] THEN RETRY ELSE CONTINUE; }; response ¬ SunYPClient.Next[h.rpcHandle, h.conversation, [domain~h.domain, map~map, key~Rope.ToRefText[keyBefore]]]; errorCode ¬ MapErrorStatus[response.status, h]; }; IF errorCode # NIL THEN ERROR Error[errorCode]; RETURN [Rope.FromRefText[response.key], response.val]; }; MapList: PUBLIC PROC [h: Handle, eachMap: SunYP.EachMapNameProc] ~ { status: SunYP.Status; errorCode: ATOM ¬ NIL; gotOne: BOOL ¬ FALSE; eachMapOuter: SunYP.EachMapNameProc -- [map] -- ~ { gotOne ¬ TRUE; RETURN [eachMap[map]]; }; { ENABLE SunRPC.Error => { errorCode ¬ MapErrorCode[code, h]; IF (NOT gotOne) AND FixupForDownServer[h] THEN RETRY ELSE CONTINUE; }; status ¬ SunYPClient.Maplist[h.rpcHandle, h.conversation, h.domain, eachMapOuter]; errorCode ¬ MapErrorStatus[status, h]; }; IF errorCode # NIL THEN ERROR Error[errorCode]; }; ExpandTextSeq: PROC [s: SunYPAgent.TextSeq] RETURNS [new: SunYPAgent.TextSeq] ~ { new ¬ NEW[SunYPAgent.TextSeqObject[2*s.maxLength]]; new.length ¬ s.length; FOR i: CARDINAL IN [0 .. s.length) DO new.refText[i] ¬ s.refText[i]; ENDLOOP; }; IsWhiteSpace: PROC [c: CHAR] RETURNS [isWhite: BOOL] ~ { OPEN Ascii; isWhite ¬ (SELECT c FROM TAB, LF, FF, CR, SP => TRUE, ENDCASE => FALSE); }; TokenizeUsingSeparator: PUBLIC PROC [in: REF READONLY TEXT, sepChar: CHAR, trimLeadingSpace, trimTrailingSpace: BOOL] RETURNS [out: SunYPAgent.TextSeq] ~ { i, iTo, len: CARDINAL; scratch: REF TEXT ¬ RefText.ObtainScratch[in.length]; CountSeparators: PROC [txt: REF READONLY TEXT, sepChar: CHAR] RETURNS [ct: CARDINAL ¬ 0] = { FOR k: CARDINAL IN [0..txt.length) DO IF txt[k] = sepChar THEN ct ¬ ct + 1; ENDLOOP; }; len ¬ CountSeparators[in, sepChar] + 1; out ¬ NEW[SunYPAgent.TextSeqObject[len]]; out.length ¬ 0; i ¬ 0; DO IF i >= in.length THEN EXIT; IF trimLeadingSpace AND IsWhiteSpace[in[i]] THEN { i ¬ i + 1; LOOP }; iTo ¬ 0; DO IF (i >= in.length) OR (in[i] = sepChar) THEN EXIT; scratch[iTo] ¬ in[i]; i ¬ i + 1; iTo ¬ iTo + 1; ENDLOOP; IF trimTrailingSpace THEN WHILE (iTo > 0) AND IsWhiteSpace[scratch[iTo-1]] DO iTo ¬ iTo - 1 ENDLOOP; scratch.length ¬ iTo; IF out.length >= out.maxLength THEN out ¬ ExpandTextSeq[out]; out.refText[out.length] ¬ RefText.Append[to~RefText.New[scratch.length], from~scratch]; out.length ¬ out.length + 1; i ¬ i + 1; ENDLOOP; scratch.length ¬ 0; FOR k: CARDINAL DECREASING IN [0..len) DO IF out.refText[k] = NIL THEN { out.refText[k] ¬ RefText.Append[to~RefText.New[scratch.length], from~scratch]; } ELSE EXIT; ENDLOOP; RefText.ReleaseScratch[scratch]; out.length ¬ len; }; Tokenize: PUBLIC PROC [in: REF READONLY TEXT] RETURNS [out: SunYPAgent.TextSeq] ~ { i, iTo: CARDINAL; scratch: REF TEXT ¬ RefText.ObtainScratch[in.length]; out ¬ NEW[SunYPAgent.TextSeqObject[8]]; out.length ¬ 0; i ¬ 0; DO IF i >= in.length THEN EXIT; iTo ¬ 0; DO IF i >= in.length THEN EXIT; IF IsWhiteSpace[in[i]] THEN EXIT; scratch[iTo] ¬ in[i]; i ¬ i + 1; iTo ¬ iTo + 1; ENDLOOP; scratch.length ¬ iTo; IF out.length >= out.maxLength THEN out ¬ ExpandTextSeq[out]; out.refText[out.length] ¬ RefText.Append[to~RefText.New[scratch.length], from~scratch]; out.length ¬ out.length + 1; i ¬ i + 1; WHILE (i < in.length) AND (IsWhiteSpace[in[i]]) DO i ¬ i + 1 ENDLOOP; ENDLOOP; RefText.ReleaseScratch[scratch]; }; lastSweepTime: BasicTime.GMT ¬ BasicTime.earliestGMT; Daemon: PROC ~ { DO thisSweepTime: BasicTime.GMT; secondsSinceLastSweep: INT; secs: CARD; -- signed/unsigned ambiguity ... bletch! thisSweepTime ¬ BasicTime.Now[ ! BasicTime.TimeNotKnown => { thisSweepTime ¬ BasicTime.Update[lastSweepTime, (msecBetweenSweeps+999)/1000]; CONTINUE; } ]; secondsSinceLastSweep ¬ BasicTime.Period[from~lastSweepTime, to~thisSweepTime]; IF secondsSinceLastSweep < 0 THEN ERROR; secs ¬ secondsSinceLastSweep; SweepCachedValues[secs]; SweepServers[secs]; SweepNullServers[secs]; Process.PauseMsec[msecBetweenSweeps]; ENDLOOP; }; IPNameProc: PROC RETURNS [name: ROPE] = { ENABLE Error => GOTO NoYP; h: Handle ¬ NIL; addressRope, unbracketedAddressRope: ROPE; hostsEntry: REF TEXT; seq: SunYPAgent.TextSeq; h ¬ ObtainHandle[NIL, NIL, Arpa.nullAddress]; addressRope ¬ Convert.RopeFromArpaAddress[Arpa.MyAddress[]]; unbracketedAddressRope ¬ Rope.Substr[addressRope, 1, Rope.Length[addressRope]-2]; hostsEntry ¬ Match[h, hostsMapName, unbracketedAddressRope]; seq ¬ Tokenize[hostsEntry]; IF seq.length <= hostNamePos THEN GOTO NoYP; name ¬ Rope.FromRefText[seq.refText[hostNamePos]]; IF h # NIL THEN ReleaseHandle[h]; EXITS NoYP => NULL; }; IPAddressProc: PROC RETURNS [ROPE] = { RETURN[Convert.RopeFromArpaAddress[Arpa.MyAddress[]]]; }; Init: PROC = { thisMachineProcs: ThisMachineRegistry.ThisMachineRef ¬ NEW[ThisMachineRegistry.ThisMachineProcsRec ¬ [ which: $ip, Name: IPNameProc, Address: IPAddressProc, ProcessorID: NIL ]]; IF Basics.IsBound[LOOPHOLE[ThisMachineRegistry.RegisterThisMachineProcs]] THEN ThisMachineRegistry.RegisterThisMachineProcs[thisMachineProcs]; }; TRUSTED { Process.Detach[ FORK Daemon[] ] }; Init[]; }... FSunYPAgentImpl.mesa Copyright Σ 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Demers, November 28, 1988 10:47:03 am PST Alan Ishigo November 29, 1988 2:24:19 pm PST Willie-sue, January 18, 1993 3:27 pm PST TODO: the null server stuff is baroque. Chauser, January 3, 1992 11:07 am PST Parameters Types Server Cache This never raises Error, may store a down server in serverTab. NOTE: ServerResponding[server] will set server.down appropriately. Raises Error if the specified server can't be contacted or doesn't serve the specified domain. Handles Match Response Cache Public Procedures Utilities Make sure each element of the seq is not NIL ... must have at least have a zero length rope Sweep Daemon and Rollback Rollback: Booting.RollbackProc ~ { SweepCachedValues[CARD.LAST]; SweepServers[CARD.LAST]; SweepNullServers[CARD.LAST]; }; Proc passed to ThisMachineRegistry to register the IP name proc with the ThisMachine interface Proc will be passed to ThisMachineRegistry to register with the ThisMachine interface. Mainline code Booting.RegisterProcs[r: Rollback]; ΚΕ–(cedarcode) style•NewlineDelimiter ˜code™Kšœ Οeœ=™HK™)K™,K™(K™K™'K™%K˜—šΟk ˜ Kšœžœ#˜-Kšœžœ˜Kš œžœžœžœžœžœžœ˜"Kšœžœ!˜-Kšœ žœžœ%˜FKšœžœ˜$Kšœžœ˜"Kšœžœ.˜;Kšœžœžœ˜:Kšœžœ ˜Kšœžœ˜&Kšœ žœ˜&Kšœ žœ,˜žœ˜RKšœ˜K˜—K˜K˜—šŸ œžœžœ žœ˜Ešžœ*žœ žœž˜BKšœžœžœžœ˜HKšžœ˜—K˜K˜—š Ÿ œžœžœžœžœ˜=Kšžœ'˜-K˜—šŸ œžœžœžœ˜TKšœ žœWžœ˜kKšœ˜K˜K˜—šŸœžœžœ žœ"˜MK˜Kšœžœ˜$šžœ žœž˜šžœ˜šžœ˜K˜"K˜K˜—šžœ˜K˜Kšžœžœžœžœ˜MK˜——K˜Kšžœ˜—K˜K˜—šŸœžœ žœžœ˜DK™>Kšœ žœ&˜5šžœžœž˜Kšœ žœžœ˜Kšœžœžœ žœ˜>Kšœžœ˜šœžœžœ'žœ˜DK™B—Kšžœ˜—Kšžœ žœžœžœ˜šΟcœ˜K˜)K˜&KšœYžœ˜cKšžœžœ3˜UK˜—šžœ žœžœ˜Kšœ˜K˜—Kšœ-˜-K˜K˜—šŸœžœ!žœžœ˜[K™^Kšœ žœžœ˜Kšœ˜Kšœožœ˜|šžœ ž˜šžœ˜Kšœ2˜2Kšœžœ˜K˜Kšœ-˜-K˜—šžœ˜Kšžœ˜K˜——K˜K˜—š Ÿœžœžœžœžœ˜NKšœ˜K˜šžœžœžœ˜"Kšžœ#žœ˜2KšœM˜MK˜Kšœ˜Kšœ˜Kšœ žœ˜K˜—Kšœžœ ˜Kšžœžœžœžœ˜+Kšžœžœžœžœ˜1K˜——™Kšœžœžœ˜Kšœž œ˜/K˜š Ÿ œžœžœžœEžœ˜ˆKšœžœ˜K˜Kšœžœ&˜;Kšžœžœžœ+˜Cšžœ˜šžœ˜šžœžœ žœž˜"šœ5˜5Kš œ žœ#žœžœžœžœ˜K—Kšžœ žœžœžœ˜Kšœ=˜=Kšžœ˜—K˜—šžœ˜Kšœ(˜(Kšžœžœžœ˜:K˜——Kšžœžœžœ&˜@KšœU˜UKšœžœ˜†K˜K˜—šŸ œžœžœT˜nK˜šžœ!˜#šžœ˜K˜&Kšžœžœžœ˜:K˜—šžœ˜Kšœ5˜5K˜——K˜%Kš œžœžœžœžœ˜Tšžœžœ˜K˜K˜NK˜—K˜K˜—šŸ œžœžœ˜*Kšžœžœžœ.žœ˜MKšœ žœ˜Kšžœžœžœ:žœ˜\K˜K˜—š Ÿœžœ žœ žœžœ˜FKšžœ žœ˜K˜Kšžœžœžœ˜!K˜K˜(Kšžœžœžœ˜&Kšžœ&žœžœ˜4Kšžœžœžœ˜Kšœžœ˜ K˜——™Kšœ žœžœ˜*šœžœžœ˜"K˜Kšœžœ˜&Kšœžœ˜Kšœžœžœ˜Kšœžœ˜ K˜K˜K˜—Kšœžœ ˜(K˜.Kšœžœ˜Kšœž œ˜ K˜K˜*K˜šŸœžœ˜6Kšœ˜šŸ œ $œ˜Išžœžœ ˜Kšžœžœ žœ˜Kšžœžœžœ˜—K˜—Kš žœ žœžœ žœžœžœ˜1Kšœ-˜-K˜K˜—šŸœžœžœ˜>šžœ˜Kšžœ˜Kšžœ˜—šžœ˜Kšžœ˜Kšžœ˜—K˜K˜—š Ÿœžœžœžœžœ˜zK˜šŸ œ $œ˜Išžœ˜Kšžœžœžœžœ˜3Kšžœžœ˜>—K˜—šŸ œ $œ˜Išžœ˜Kšžœžœžœžœ˜3Kšžœžœ˜>—K˜—K˜4Kšœ.˜.Kšœžœ"˜-šžœžœž˜šœžœ˜šž˜šžœžœž˜šœ(˜(Kšœžœ˜K˜&Kšžœ˜K˜—šœžœ˜Kšžœ˜Kšžœ˜K˜—šžœ˜ K˜K˜K˜Kšžœ˜K˜——Kšžœ˜K˜K˜ Kšœ žœ˜Kšœ˜—K˜—˜K˜K˜—Kšžœ˜—Kšœ˜K˜K˜—š Ÿ œžœžœžœžœžœ#˜dšžœ žœžœ˜Kšžœ žœžœ˜K˜Kšœ(˜(K˜—šžœžœ˜%šžœž˜šžœ˜Kšœžœ˜K˜'Kšžœ˜K˜—šžœ˜K˜Kšœ žœ˜K˜K˜K˜——K˜—K˜K˜—šŸœžœžœ"˜@Kšœ žœ˜ Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœ&˜&Kšžœ˜K˜K˜—šŸœžœžœ4˜QKšžœ žœžœ˜Kšœ˜K˜K˜—šŸœžœžœ žœ#˜OK˜šžœžœžœž˜.K˜šžœ˜šžœ˜K˜K˜—šžœ˜Kšžœ žœžœ˜Kšœ˜Kšœ˜Kšœ˜K˜——Kšžœ˜—K˜——™š Ÿ œžœžœ žœžœ˜IKšœžœ˜K˜ K˜K˜K˜—šŸœžœ#žœžœ˜UKšœžœ˜K˜ šœžœž˜ Kšœžœ˜ K˜K˜K˜K˜Kšžœ˜—K˜K˜—šŸœžœžœžœžœžœžœžœ˜PK˜Kšœ žœ˜K˜K˜+šžœ žœžœ˜K˜Kšœ˜Kšžœ˜K˜—šœ˜šžœ˜Kšœ"˜"Kš žœžœžœžœžœ˜2K˜—K˜oKšœ/˜/K˜—šžœ ž˜šžœ˜K˜Kšœ˜Kšžœ˜K˜—šžœ˜K˜Kšžœ˜K˜——K˜K˜—šŸœžœžœžœžœžœžœžœ˜PK˜Kšœ žœžœ˜˜šžœ˜Kšœ"˜"Kš žœžœžœžœžœ˜2K˜—K˜VKšœ/˜/K˜—Kšžœ žœžœžœ˜/Kšžœ0˜6K˜K˜—šŸœžœžœžœ žœžœžœžœžœ˜`K˜Kšœ žœžœ˜˜šžœ˜Kšœ"˜"Kš žœžœžœžœžœ˜2K˜—K˜tKšœ/˜/K˜—Kšžœ žœžœžœ˜/Kšžœ0˜6K˜K˜—šŸœžœžœ0˜DK˜Kšœ žœžœ˜Kšœžœžœ˜šœ$  œ˜3Kšœ žœ˜Kšžœ˜K˜—˜šžœ˜Kšœ"˜"Kšžœžœ žœžœžœžœžœ˜CK˜—K˜RKšœ&˜&K˜—Kšžœ žœžœžœ˜/K˜——™ šŸ œžœžœ˜QKšœžœ*˜3K˜šžœžœžœž˜%K˜Kšžœ˜—K˜K˜—š Ÿ œžœžœžœ žœ˜8Kšžœ˜ šœ žœž˜Kš žœžœžœžœžœžœ˜Kšžœžœ˜—K˜K˜—šŸœžœžœžœžœžœ žœ'žœžœ˜—Kšœ˜Kšœ žœ˜Kšœ žœžœ$˜5K˜KšŸœžœžœžœžœ žœžœžœ ˜\Kšœžœžœžœž˜(Kšœžœžœ ˜,Kšœžœ˜K˜ Kšœ3˜3Kšœžœ"˜+K˜K˜šž˜Kšžœžœžœ˜Kšžœžœžœžœ˜EK˜šž˜Kšžœžœžœžœ˜3K˜K˜ K˜Kšžœ˜—šžœž˜Kšžœ žœžœžœ˜J—K˜Kšžœžœ˜=K˜WK˜K˜ Kšžœ˜—K™[K˜Kš žœžœž œžœ ž˜)Kšœžœžœžœ˜ K˜RKšœžœžœ˜Kšœžœ˜ K˜ K˜K˜K˜—šŸœžœžœžœžœžœžœ˜OKšœ˜Kšœžœ˜Kšœ žœžœ$˜5Kšœžœ˜'K˜K˜šž˜Kšžœžœžœ˜K˜šž˜Kšžœžœžœ˜Kšžœžœžœ˜!K˜K˜ K˜Kšžœ˜—K˜Kšžœžœ˜=K˜WK˜K˜ Kšžœžœžœ žœ˜EKšžœ˜—K˜ K˜K˜——™Kšœžœ˜5K˜šŸœžœ˜šž˜Kšœžœ˜Kšœžœ˜Kšœžœ (˜4šœ˜šœ˜KšœN˜NKšžœ˜ Kšœ˜—Kšœ˜—KšœO˜OKšžœžœžœ˜(Kšœ˜Kšœ˜K˜K˜Kšœ%˜%Kšžœ˜—K˜K˜—šŸœ™"Kšœžœžœ™Kšœ žœžœ™Kšœžœžœ™K™K™—K™_šŸ œžœžœžœ˜)Kšžœ žœ˜Kšœ žœ˜Kšœ%žœ˜*Kšœ žœžœ˜šœ˜K˜—Kšœžœžœ˜-Kšœ<˜