DIRECTORY BasicTime USING [earliestGMT, GMT, latestGMT, Now, Period, Update], CHEntriesP0V0 USING [addressList, members], CHOpsP2V3 USING [ArgumentError, AuthenticationError, Authenticator, CallError, ListDomainsServed, PropertyError, RetrieveItem, RetrieveMembers, UpdateError, WrongServer], CrRPC USING [BulkDataCheckAbortProc, BulkDataXferProc, CreateClientHandle, DestroyClientHandle, Error, Handle], Process USING [Detach, EnableAborts, SecondsToTicks, SetTimeout], Random USING [ChooseInt], Rope USING [Cat, Index, IsEmpty, ROPE, Substr], SymTab USING [Create, EachPairAction, Fetch, Pairs, Ref, Store], XNS USING [Address, Host, unknownAddress, unknownHost, unknownSocket], XNSAuth USING [Conversation, GetCredentials, GetNextVerifier, GetNullIdentity, Identity, Initiate, SetRecipientHostNumber, Terminate], XNSCH USING [AddressesFromItem, BestAddressInList, Element, Error, ErrorCode, Item, Name, Pattern, Properties, PropertyID, Which], XNSCHPrivate USING [BulkDataValueProc, ReadBulkDataStream, ReadElement, ReadRope, RemoteProc], XNSRouter USING [GetHops, unreachable], XNSServerLocation USING [EachAddressProc, LocateServers, StopBroadcast], XNSWKS USING [clearinghouse] ; XNSCHPrivateImpl: CEDAR MONITOR IMPORTS BasicTime, CHOpsP2V3, CrRPC, Process, Random, Rope, SymTab, XNSAuth, XNSCH, XNSCHPrivate, XNSRouter, XNSServerLocation EXPORTS XNSCH, XNSCHPrivate ~ { OPEN CHEntries: CHEntriesP0V0, CHOps: CHOpsP2V3; chPgmNum: CARD ~ 2; chVersionNum: CARDINAL ~ 3; CARD16: TYPE ~ CARDINAL; -- temporary until Mimosa ???? ROPE: TYPE ~ Rope.ROPE; Name: TYPE ~ XNSCH.Name; Pattern: TYPE ~ XNSCH.Pattern; Element: TYPE ~ XNSCH.Element; PropertyID: TYPE ~ XNSCH.PropertyID; Properties: TYPE ~ XNSCH.Properties; Item: TYPE ~ XNSCH.Item; WrongServerError: PUBLIC ERROR [hint: Name] _ CHOps.WrongServer; shiftInName: ROPE ~ "CHServers"; chServiceName: Name ~ [organization~shiftInName, domain~shiftInName, object~"Clearinghouse Service"]; Conversation: TYPE ~ REF ConversationObject; ConversationObject: PUBLIC TYPE ~ RECORD [ conversation: XNSAuth.Conversation, host: XNS.Host _ XNS.unknownHost, handle: CrRPC.Handle _ NIL ]; InitiateConversation: PUBLIC PROC [identity: XNSAuth.Identity, server: XNS.Address] RETURNS [c: Conversation] ~ { c _ NEW[ ConversationObject _ [] ]; IF identity = NIL THEN identity _ XNSAuth.GetNullIdentity[]; c.conversation _ XNSAuth.Initiate[identity, chServiceName]; IF server # XNS.unknownAddress THEN { c.handle _ CrRPC.CreateClientHandle[$SPP, server]; c.host _ server.host }; }; TerminateConversation: PUBLIC PROC [c: Conversation] ~ { IF c.handle # NIL THEN CrRPC.DestroyClientHandle[c.handle]; XNSAuth.Terminate[c.conversation] }; GetAuthenticator: PUBLIC PROC [c: Conversation, host: XNS.Host] RETURNS [CHOps.Authenticator] ~ { XNSAuth.SetRecipientHostNumber[c.conversation, host]; RETURN [ [XNSAuth.GetCredentials[c.conversation], XNSAuth.GetNextVerifier[c.conversation]] ] }; IsGeneric: PUBLIC PROC [c: Conversation] RETURNS [BOOL] ~ { RETURN [c.handle = NIL] }; CachedServer: TYPE ~ REF CachedServerObject; CachedServerObject: TYPE ~ RECORD [ next: CachedServer _ NIL, -- successor in serversByAddress. ML address: XNS.Address, -- readonly knowDomainsServed: BOOL _ FALSE, -- hint, no ML referenced: BasicTime.GMT, -- ML inactiveUntil: BasicTime.GMT -- ML ]; cachedServerTimeout: INT _ 8*LONG[3600]; -- seconds infiniteTimeout: INT _ 07FFFFFFFH; numHeaders: CARDINAL ~ 101; HeaderIndex: TYPE ~ [0..numHeaders); Headers: TYPE ~ REF HeadersObject; HeadersObject: TYPE ~ ARRAY HeaderIndex OF CachedServer; serversByAddress: Headers ~ NEW[HeadersObject _ ALL[NIL]]; HashHost: PROC [host: XNS.Host] RETURNS [HeaderIndex] ~ { acc: CARD _ ((((host.a*5+host.b)*5+host.c)*5+host.d)*5+host.e)*5+host.f; RETURN [acc MOD numHeaders] }; GetServerByAddress: ENTRY PROC [addr: XNS.Address, makeActive: BOOL] RETURNS [s: CachedServer] ~ { ENABLE UNWIND => NULL; index: HeaderIndex ~ HashHost[addr.host]; addr.socket _ XNS.unknownSocket; FOR s _ serversByAddress^[index], s.next WHILE s # NIL DO IF s.address = addr THEN { IF makeActive THEN s.inactiveUntil _ BasicTime.earliestGMT; RETURN }; ENDLOOP; s _ serversByAddress^[index] _ NEW[CachedServerObject _ [next~serversByAddress^[index], address~addr, referenced~BasicTime.Now[], inactiveUntil~BasicTime.earliestGMT]]; }; ApplyToAllServers: INTERNAL PROC [proc: PROC[CachedServer]] ~ { FOR index: HeaderIndex IN [HeaderIndex.FIRST .. HeaderIndex.LAST] DO FOR s: CachedServer _ serversByAddress^[index], s.next WHILE s # NIL DO proc[s]; ENDLOOP; ENDLOOP; }; secondsBusy: INT _ 10; secondsDown: INT _ 600; MarkServerUsable: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil _ BasicTime.earliestGMT }; MarkServerBusy: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil _ BasicTime.Update[BasicTime.Now[], secondsBusy] }; MarkServerDown: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil _ BasicTime.Update[BasicTime.Now[], secondsDown]; s.knowDomainsServed _ FALSE }; MarkServerNonexistent: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil _ BasicTime.latestGMT; s.knowDomainsServed _ FALSE }; ServerIsDead: INTERNAL PROC [s: CachedServer, timeout: INT] RETURNS [BOOL] ~ { IF s.inactiveUntil = BasicTime.latestGMT THEN RETURN [TRUE]; IF BasicTime.Period[from~s.referenced, to~BasicTime.Now[]] > timeout THEN RETURN [TRUE]; RETURN [FALSE] }; SweepAllServers: ENTRY PROC ~ { p, prev: CachedServer; FOR i: HeaderIndex IN [HeaderIndex.FIRST .. HeaderIndex.LAST] DO p _ serversByAddress^[i]; prev _ NIL; WHILE p # NIL DO IF ServerIsDead[p, infiniteTimeout] -- infinite => everybody believes it's dead THEN { p _ p.next; IF prev = NIL THEN serversByAddress^[i] _ p ELSE prev.next _ p } ELSE { prev _ p; p _ p.next }; ENDLOOP; ENDLOOP; }; CachedServerListHead: TYPE ~ REF CachedServerList; CachedServerList: TYPE ~ REF CachedServerListElement; CachedServerListElement: TYPE ~ RECORD [ next: CachedServerList, -- ML server: CachedServer -- readonly ]; AddServerToList: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer] RETURNS [new: BOOL] ~ { ENABLE UNWIND => NULL; new _ AddServerToListInternal[listHead, server] }; AddServerToListInternal: INTERNAL PROC [listHead: CachedServerListHead, server: CachedServer] RETURNS [new: BOOL] ~ { FOR p: CachedServerList _ listHead^, p.next WHILE p # NIL DO IF p.server = server THEN RETURN [FALSE]; ENDLOOP; listHead^ _ NEW[CachedServerListElement _ [next~listHead^, server~server]]; RETURN [TRUE] }; DeleteServerFromListInternal: INTERNAL PROC [listHead: CachedServerListHead, server: CachedServer] ~ { p, prev: CachedServerList _ NIL; IF listHead = NIL THEN RETURN; p _ listHead^; WHILE (p # NIL) AND (p.server # server) DO prev _ p; p _ p.next ENDLOOP; IF p = NIL THEN RETURN; IF prev = NIL THEN listHead^ _ p ELSE prev.next _ p; }; ServerFilterProc: TYPE ~ PROC [CachedServer] RETURNS [ok: BOOL]; GetBestServerFromList: ENTRY PROC [listHead: CachedServerListHead, filter: ServerFilterProc _ NIL] RETURNS [bestServer: CachedServer _ NIL] ~ { ENABLE UNWIND => NULL; bestHops: CARDINAL _ LAST[CARDINAL]; IF listHead = NIL THEN RETURN [NIL]; FOR p: CachedServerList _ listHead^, p.next WHILE p # NIL DO server: CachedServer ~ p.server; hops: CARDINAL; IF BasicTime.Period[from: BasicTime.Now[], to: server.inactiveUntil] > 0 THEN LOOP; hops _ XNSRouter.GetHops[server.address.net]; IF hops >= bestHops THEN LOOP; IF hops >= XNSRouter.unreachable THEN LOOP; IF filter # NIL THEN IF NOT filter[server].ok THEN LOOP; bestServer _ server; bestHops _ hops; ENDLOOP; IF bestServer # NIL THEN bestServer.referenced _ BasicTime.Now[]; }; SweepList: INTERNAL PROC [listHead: CachedServerListHead, timeout: INT] ~ { p, prev: CachedServerList _ NIL; IF listHead = NIL THEN RETURN; p _ listHead^; WHILE p # NIL DO IF ServerIsDead[p.server, timeout] THEN { p _ p.next; IF prev = NIL THEN listHead^ _ p ELSE prev.next _ p } ELSE { prev _ p; p _ p.next }; ENDLOOP; }; domainServers: SymTab.Ref ~ SymTab.Create[mod~numHeaders, case~FALSE]; GetServersForDomain: PROC [domain: ROPE] RETURNS [CachedServerListHead] ~ { RETURN [NARROW[SymTab.Fetch[domainServers, domain].val]] }; AddServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ { ENABLE UNWIND => NULL; listHead: CachedServerListHead; listHead _ NARROW[SymTab.Fetch[domainServers, domain].val]; IF listHead = NIL THEN { listHead _ NEW[CachedServerList _ NIL]; [] _ SymTab.Store[domainServers, domain, listHead] }; [] _ AddServerToListInternal[listHead, server] }; DeleteServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ { ENABLE UNWIND => NULL; listHead: CachedServerListHead ~ NARROW[SymTab.Fetch[domainServers, domain].val]; DeleteServerFromListInternal[listHead, server] }; SweepDomainCache: ENTRY PROC ~ { EachPair: INTERNAL SymTab.EachPairAction -- [key, val] RETURNS[BOOL] -- ~ { IF val # NIL THEN SweepList[NARROW[val], cachedServerTimeout]; RETURN[FALSE] }; [] _ SymTab.Pairs[domainServers, EachPair]; }; ListDomainsIKnow: ENTRY PROC RETURNS [domains: LIST OF ROPE] ~ { EachPair: INTERNAL SymTab.EachPairAction -- [key, val] RETURNS[BOOL] -- ~ { domains _ CONS[key, domains]; RETURN[FALSE] }; [] _ SymTab.Pairs[domainServers, EachPair]; }; LearnDomainsServed: PROC [h: CrRPC.Handle, theServer: CachedServer] ~ { c: Conversation; ReadStreamOfDomainNames: CrRPC.BulkDataXferProc ~ { EachDomainNameInStream: XNSCHPrivate.BulkDataValueProc ~ { domPart, orgPart, key: ROPE; orgPart _ XNSCHPrivate.ReadRope[stream]; domPart _ XNSCHPrivate.ReadRope[stream]; key _ Rope.Cat[orgPart, ":", domPart]; AddServerForDomain[key, theServer]; AddServerForDomain[OrganizationFromDomain[key], theServer]; -- <- BOGUS ??? }; RETURN [XNSCHPrivate.ReadBulkDataStream[h, stream, checkAbort, EachDomainNameInStream]]; }; IF theServer.knowDomainsServed THEN RETURN; c _ InitiateConversation[NIL, XNS.unknownAddress]; CHOps.ListDomainsServed[h, ReadStreamOfDomainNames, GetAuthenticator[c, theServer.address.host]]; theServer.knowDomainsServed _ TRUE; TerminateConversation[c]; }; OrganizationFromDomain: PROC [domain: ROPE] RETURNS [organization: ROPE] ~ { len: INT ~ Rope.Index[s1~domain, s2~":"]; organization _ Rope.Cat[shiftInName, ":", Rope.Substr[domain, 0, len]] }; nearbyServers: CachedServerListHead ~ NEW[CachedServerList _ NIL]; defaultMaxHops: CARDINAL _ 3; desperationContactMaxHops: CARDINAL _ 6; desperationBroadcastMaxHops: CARDINAL _ 5; desperationBroadcastTryLimit: CARDINAL _ 2; sweepBroadcastTryLimit: CARDINAL _ 1; maxHopsUsedForNearby: CARDINAL _ 0; RememberHopsUsedForNearby: ENTRY PROC [hops: CARDINAL] ~ INLINE { maxHopsUsedForNearby _ MAX[maxHopsUsedForNearby, hops] }; GetHopsUsedForNearby: ENTRY PROC RETURNS [hops: CARDINAL] ~ INLINE { hops _ maxHopsUsedForNearby; maxHopsUsedForNearby _ 0 }; BroadcastForNearbyServers: PROC [ maxHops: CARDINAL, nWanted: CARDINAL _ CARDINAL.LAST, tryLimit: CARDINAL _ 0] ~ { nGot: CARDINAL _ 0; EachAddress: XNSServerLocation.EachAddressProc -- [addr: XNS.Address] -- ~ { server: CachedServer; server _ GetServerByAddress[addr~addr, makeActive~TRUE]; IF AddServerToList[nearbyServers, server].new THEN nGot _ nGot.SUCC; IF nGot = nWanted THEN ERROR XNSServerLocation.StopBroadcast[] }; XNSServerLocation.LocateServers[socket~XNSWKS.clearinghouse, remotePgm~chPgmNum, remotePgmVersion~chVersionNum, eachAddress~EachAddress, maxHops~maxHops, tryLimit~tryLimit]; }; StopSearch: ERROR ~ CODE; NoticeKnownNearbyServers: ENTRY PROC[ maxHops: CARDINAL, nWanted: CARDINAL _ CARDINAL.LAST] ~ { nGot: CARDINAL _ 0; EachServer: INTERNAL PROC [s: CachedServer] ~ { IF XNSRouter.GetHops[s.address.net] > maxHops THEN RETURN; IF AddServerToListInternal[nearbyServers, s].new THEN nGot _ nGot.SUCC; IF nGot = nWanted THEN ERROR StopSearch[] }; ApplyToAllServers[proc~EachServer ! StopSearch => CONTINUE]; }; GetBestKnownServer: ENTRY PROC [maxHops: CARDINAL, filter: ServerFilterProc] RETURNS [bestServer: CachedServer _ NIL] ~ { EachServer: INTERNAL PROC [s: CachedServer] ~ { hops: CARDINAL; IF BasicTime.Period[from: BasicTime.Now[], to: s.inactiveUntil] > 0 THEN RETURN; hops _ XNSRouter.GetHops[s.address.net]; IF hops > maxHops THEN RETURN; IF (filter # NIL) AND (NOT filter[s]) THEN RETURN; maxHops _ hops; bestServer _ s }; ApplyToAllServers[proc~EachServer]; }; GetBestServer: PROC [filter: ServerFilterProc _ NIL, okToBroadcast: BOOL] RETURNS [bestServer: CachedServer, didBroadcast: BOOL _ FALSE] ~ { bestServer _ GetBestServerFromList[nearbyServers, filter]; IF bestServer = NIL THEN { NoticeKnownNearbyServers[maxHops~defaultMaxHops]; bestServer _ GetBestServerFromList[nearbyServers, filter] }; IF bestServer = NIL THEN { bestServer _ GetBestKnownServer[desperationContactMaxHops, filter] }; IF (bestServer = NIL) AND okToBroadcast THEN { BroadcastForNearbyServers[maxHops~desperationBroadcastMaxHops, nWanted~1, tryLimit~desperationBroadcastTryLimit]; didBroadcast _ TRUE; bestServer _ GetBestServerFromList[nearbyServers, filter] }; IF bestServer # NIL THEN RememberHopsUsedForNearby[XNSRouter.GetHops[bestServer.address.net]] ELSE RememberHopsUsedForNearby[CARDINAL.LAST]; }; SweepNearbyServers: PROC ~ { maxHopsUsed: CARDINAL; EntrySweepNearbyServers[]; NoticeKnownNearbyServers[maxHops~defaultMaxHops]; IF (maxHopsUsed _ GetHopsUsedForNearby[]) > 0 THEN TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~MIN[maxHopsUsed, defaultMaxHops], tryLimit~sweepBroadcastTryLimit] ]; }; }; EntrySweepNearbyServers: ENTRY PROC ~ { SweepList[nearbyServers, infiniteTimeout]; }; maxHints: CARDINAL ~ 25; ProcessHint: PROC [h: CrRPC.Handle, theServer: CachedServer, hint: Name] RETURNS [servers: CachedServerListHead] ~ { c: Conversation; nRead: CARDINAL; hints: ARRAY [1..maxHints] OF ROPE _ ALL[NIL]; ReadStreamOfNames: CrRPC.BulkDataXferProc ~ { EachNameInStream: XNSCHPrivate.BulkDataValueProc ~ { serverName: Name ~ XNSCHPrivate.ReadElement[stream]; nRead _ nRead + 1; IF nRead <= maxHints THEN { hints[nRead] _ serverName.object; RETURN }; IF Random.ChooseInt[NIL, 1, nRead] > maxHints THEN RETURN; hints[Random.ChooseInt[NIL, 1, maxHints]] _ serverName.object; }; RETURN [XNSCHPrivate.ReadBulkDataStream[h, stream, checkAbort, EachNameInStream]]; }; c _ InitiateConversation[NIL, XNS.unknownAddress]; nRead _ 0; [] _ CHOps.RetrieveMembers[h, hint, CHEntries.members, ReadStreamOfNames, GetAuthenticator[c, theServer.address.host] ! CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError => CONTINUE]; servers _ NEW[CachedServerList _ NIL]; FOR i: CARDINAL IN [1 .. MIN[nRead, maxHints]] DO { ENABLE CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError => CONTINUE; item: Item; addresses: LIST OF XNS.Address; [value~item] _ CHOps.RetrieveItem[h, [organization~shiftInName, domain~shiftInName, object~hints[i]], CHEntries.addressList, GetAuthenticator[c, theServer.address.host]]; addresses _ XNSCH.AddressesFromItem[item]; IF addresses # NIL THEN { server: CachedServer ~ GetServerByAddress[ addr~XNSCH.BestAddressInList[addresses], makeActive~FALSE]; [] _ AddServerToList[servers, server] }; }; ENDLOOP; TerminateConversation[c]; }; maxTries: CARDINAL ~ 15; triesToUse: CARDINAL _ 7; -- Bounds Fault if this exceeds maxTries CallRemote: PUBLIC PROC [c: Conversation, proc: XNSCHPrivate.RemoteProc, domain: ROPE _ NIL, idempotent: BOOL _ TRUE] ~ { State: TYPE ~ { dom, hint, org, nearby, none }; state: State _ none; organization: ROPE _ NIL; theServer: CachedServer _ NIL; theHandle: CrRPC.Handle _ NIL; gotHint: BOOL; theHint: Name; hintServers: CachedServerListHead _ NIL; specific: BOOL ~ NOT IsGeneric[c]; inClientProc: BOOL _ FALSE; didBroadcast: BOOL _ FALSE; nAlreadyUsed: CARDINAL _ 0; alreadyUsedCache: ARRAY [0..maxTries) OF CachedServer; NoteServerAlreadyUsed: PROC [server: CachedServer] ~ { alreadyUsedCache[nAlreadyUsed] _ server; nAlreadyUsed _ nAlreadyUsed + 1; }; ServerUsable: PROC [server: CachedServer] RETURNS [BOOL] ~ { FOR i: CARDINAL IN [0..nAlreadyUsed) DO IF alreadyUsedCache[i] = server THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE] }; CleanUp: PROC ~ { IF theHandle # NIL THEN CrRPC.DestroyClientHandle[theHandle]; theHandle _ NIL }; GetServerCandidate: PROC ~ { state _ none; theServer _ NIL; IF theHandle # NIL THEN { CrRPC.DestroyClientHandle[theHandle]; theHandle _ NIL }; IF NOT Rope.IsEmpty[domain] THEN { state _ dom; theServer _ GetBestServerFromList[GetServersForDomain[domain], ServerUsable]; IF theServer = NIL THEN { state _ hint; theServer _ GetBestServerFromList[hintServers, ServerUsable] }; IF theServer = NIL THEN { state _ org; IF organization = NIL THEN organization _ OrganizationFromDomain[domain]; theServer _ GetBestServerFromList[GetServersForDomain[organization], ServerUsable]; }; }; IF theServer = NIL THEN { state _ nearby; [theServer, didBroadcast] _ GetBestServer[ServerUsable, (NOT didBroadcast)] }; IF theServer = NIL THEN { state _ none; RETURN }; theHandle _ CrRPC.CreateClientHandle[$SPP, theServer.address, 90000]; -- BOGUS TIMEOUT ???? }; FOR tryNo: CARDINAL IN [1..triesToUse] DO ENABLE { UNWIND => CleanUp[]; CHOps.AuthenticationError => { ERROR XNSCH.Error[credentialsInvalid, first] }; CHOps.CallError => { SELECT problem FROM accessRightsInsufficient => { ERROR XNSCH.Error[credentialsTooWeak, first] }; tooBusy => { IF specific THEN ERROR XNSCH.Error[serverTooBusy, first]; MarkServerBusy[theServer]; LOOP }; serverDown => { IF specific THEN ERROR XNSCH.Error[allDown, first]; EXIT }; ENDCASE => -- can't happen -- { IF specific THEN ERROR XNSCH.Error[unknown, first]; MarkServerNonexistent[theServer]; LOOP }; }; CHOps.ArgumentError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM illegalProperty => illegalPropertyID, illegalOrganizationName => illegalOrganizationName, illegalDomainName => illegalDomainName, illegalObjectName => illegalObjectName, noSuchOrganization => noSuchOrganization, noSuchDomain => noSuchDomain, noSuchObject => noSuchObject ENDCASE => unknown); ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] }; CHOps.PropertyError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM missing => propertyIDNotFound, wrongType => wrongPropertyType ENDCASE => unknown); ERROR XNSCH.Error[code, first] }; CHOps.UpdateError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM noChange => noChange, outOfDate => outOfDate, objectOverflow => overflowOfName, databaseOverflow => overflowOfDataBase ENDCASE => unknown); ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] }; CrRPC.Error => { SELECT errorReason FROM courierVersionMismatch => { IF specific THEN ERROR XNSCH.Error[serviceNotExported, first]; MarkServerNonexistent[theServer]; LOOP }; rejectedNoSuchProgram, rejectedNoSuchVersion, rejectedNoSuchProcedure, rejectedUnspecified => { IF specific THEN ERROR XNSCH.Error[serviceNotExported, first]; MarkServerDown[theServer]; LOOP }; rejectedInvalidArgument => { IF specific THEN ERROR XNSCH.Error[protocolError, first]; MarkServerDown[theServer]; LOOP }; resultsTooShort, timeout, protocolError, remoteClose, communicationFailure, remoteError => -- can't retry, progress made -- { IF specific THEN ERROR XNSCH.Error[ (SELECT errorReason FROM communicationFailure => communicationFailure, ENDCASE => protocolError), first]; MarkServerDown[theServer]; IF (NOT idempotent) AND (inClientProc) THEN ERROR XNSCH.Error[wasUpNowDown, first]; LOOP }; unknown, unknownClass, argsTooLong, notImplemented, notServerHandle, notClientHandle, notBroadcastHandle -- can't happen -- => ERROR; ENDCASE => ERROR; }; CHOps.WrongServer => { IF specific THEN REJECT; MarkServerNonexistent[theServer]; LOOP }; }; IF specific THEN { inClientProc _ TRUE; proc[c.handle, c.host]; inClientProc _ FALSE; RETURN }; GetServerCandidate[]; -- sets [theServer, theHandle, state] IF state = none THEN EXIT; IF NOT theServer.knowDomainsServed THEN LearnDomainsServed[theHandle, theServer]; gotHint _ FALSE; inClientProc _ TRUE; proc[theHandle, theServer.address.host ! CHOps.WrongServer => { theHint _ hint; gotHint _ TRUE; CONTINUE } ]; inClientProc _ FALSE; IF gotHint THEN { DeleteServerForDomain[domain, theServer]; -- correct the cache IF tryNo >= triesToUse THEN EXIT; hintServers _ ProcessHint[theHandle, theServer, theHint]; NoteServerAlreadyUsed[theServer]; LOOP }; NoteServerAlreadyUsed[theServer]; AddServerForDomain[domain, theServer]; CleanUp[]; RETURN; ENDLOOP; CleanUp[]; ERROR XNSCH.Error[allDown, first]; }; daemonWakeup: CONDITION; secondsBetweenSweeps: CARDINAL _ 900; Daemon: PROC ~ { DO WaitDaemonWakeup[]; SweepDomainCache[]; SweepNearbyServers[]; SweepAllServers[]; ENDLOOP; }; WaitDaemonWakeup: ENTRY PROC ~ { TRUSTED { Process.EnableAborts[@daemonWakeup]; Process.SetTimeout[@daemonWakeup, Process.SecondsToTicks[secondsBetweenSweeps]] }; WAIT daemonWakeup; }; nNearbyServersToStart: CARDINAL _ 2; TRUSTED { Process.Detach[FORK Daemon[]] }; TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~defaultMaxHops, nWanted~nNearbyServersToStart]] }; }. ΒXNSCHPrivateImpl.mesa Demers, December 13, 1986 3:45:29 pm PST To Do: Check whether CrRPCRuntime support catches errors in bulk data i/o procs. To Do: Consider a cached serverName -> address map for hint processing? Conversations Server Objects Net and host are significant, socket is set to XNS.unknownSocket Lists of Servers ListIsEmpty: PROC [listHead: CachedServerListHead] RETURNS [isEmpty: BOOL] ~ { RETURN [listHead^ = NIL] }; ListContains: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer] RETURNS [isOnList: BOOL] ~ { IF listHead = NIL THEN RETURN [FALSE]; FOR s: CachedServerList _ listHead^, s.next DO IF s = NIL THEN RETURN [FALSE]; IF s.server = server THEN RETURN [TRUE] ENDLOOP; }; AddNewServerToList: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer] ~ { Precondition: server is not already on list. ENABLE UNWIND => NULL; listHead^ _ NEW[CachedServerListElement _ [next~listHead^, server~server]]; }; Called by Daemon. Cache of Servers by Domain Called by Daemon. For debugging. [h: Handle, stream: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL]; [stream: IO.STREAM] RETURNS [abort: BOOL _ FALSE] e.g. "Xerox:PARC" -> "CHServers:Xerox" Cache of Nearby Servers Called by Daemon. Hint Processing [h: Handle, stream: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL]; [stream: IO.STREAM] RETURNS [abort: BOOL _ FALSE] Invoking a remote procedure Precondition: NOT ServerAlreadyUsed[server] Body of CallRemote ... If this server serves the right domain and can't answer, assume no other server can answer either. This is overly pessimistic. Doing better would be at least a little bit hard. useCourier, other, Daemon Mainline Code Κ3˜code™K™(K™K™PK™GK˜—šΟk ˜ Kšœ œ œ"˜CKšœœ˜+Kšœ œ›˜ͺKšœœd˜oKšœœ4˜AKšœœ ˜Kšœœœ ˜/Kšœœ4˜@Kšœœ=˜FKšœœy˜†Kšœœw˜‚Kšœ œL˜^Kšœ œ˜'Kšœœ1˜HKšœœ˜Kšœ˜K˜—šΟnœœ˜KšœFœ,˜~Kšœœ˜K˜Kšœž œžœ ˜0K˜Kšœ œ˜Kšœœ˜K˜KšœœœΟc˜7K˜Kšœœœ˜K˜Kšœœœ˜Kšœ œœ ˜Kšœ œœ ˜Kšœ œœ ˜$Kšœ œœ ˜$Kšœœœ˜K˜Kšžœœœ"˜@K˜Kšœ œ˜ Kšœe˜eK˜head™ Kšœœœ˜,šœœœœ˜*K˜#Kšœœœ ˜!Kšœ˜K˜K˜—š žœœœ&œ œ˜qKšœœ˜#Kšœ œœ&˜Kšœœ˜—Kšœ+˜+K˜K˜—šžœœœœ œœœ˜@K™šžœœŸœ˜KKšœ œœœ˜.—Kšœ+˜+K˜K˜—šžœœ/˜GK˜šžœ˜/Kš œœœ&œ œ™YK˜šžœ ˜6Kš œ œœœ œœ™1Kšœ˜Kšœœ˜Kšœ(˜(K˜(K˜&Kšœ#˜#Kšœ<Ÿ˜KKšœ˜—KšœR˜XKšœ˜—Kšœœœ˜+Kšœœœ˜2Kšœa˜aKšœœ˜#Kšœ˜K˜K˜—š žœœ œœœ˜LK™&Kšœœ!˜)KšœI˜IK˜——™Kšœ&œœ˜BK˜Kšœœ˜Kšœœ˜(Kšœœ˜*Kšœœ˜+Kšœœ˜%K˜Kšœœ˜#š žœœœœœ˜AKšœœ˜9—š žœœœœœœ˜DKšœ8˜8—K˜šžœœ œ œœœ œ ˜sKšœœ˜šž œ$Ÿœ˜LK˜Kšœ2œ˜8Kšœ,œ œ˜DKšœœœ%˜A—Kšœ'œ€˜­K˜K˜—Kšž œœœ˜šžœœœ œ œœœ˜_Kšœœ˜šž œœœ˜/Jšœ,œœ˜:Jšœ/œ œ˜GJšœœœ˜,—Jšœ2œ˜Kšœ˜—KšœL˜RKšœ˜—Kšœœœ˜2Kšœ ˜ šœu˜uKšœAœ˜K—Kšœ œœ˜&š œœœœ˜1šœ˜šœ?˜EKšœ˜ —K˜ Kšœ œœœ ˜Kšœͺ˜ͺKšœ œ˜*šœ œœ˜Kšœ0œ*œ˜fKšœ(˜(—K˜—Kšœ˜—Kšœ˜K˜K˜——™Kšœ œ˜Kšœ œŸ(˜BK˜šž œœœ:œœœœ˜yKšœœ$˜/K˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœ œ˜K˜Kšœ$œ˜(Kšœ œœ˜"Kšœœœ˜Kšœœœ˜K˜Kšœœ˜Kšœœœ˜6K˜šžœœ˜6Kšœ+™+Kšœ(˜(Kšœ ˜ K˜—šž œœœœ˜<šœœœ˜'Kšœœœœ˜4Kšœ˜—Kšœœ˜—K˜šžœœ˜Kšœ œœ&˜=Kšœ œ˜K˜—šžœœ˜K˜ Kšœ œ˜Kšœ œœ5œ˜Ršœœœ˜"K˜ KšœM˜Mšœ œœ˜K˜ Kšœ?˜?—šœ œœ˜K˜ Kšœœœ/˜IKšœS˜SK˜—K˜—šœ œœ˜K˜Kšœ9œ˜N—šœ œœ˜Kšœ ˜ Kšœ˜ —KšœFŸ˜[K˜K˜—K™K˜šœœœ˜)šœ˜Kšœ˜šœ˜Kšœœ!˜,Kšœ˜—šœ˜šœ ˜šœ˜Kšœœ$˜/—šœ ˜ Kšœ œœœ˜9K˜Kšœ˜—šœ˜Kšœ œœœ˜3K™²Kšœ˜—Kšœ™šœŸœ˜Jšœ œœœ˜3K˜!Kšœ˜——K˜—˜šœœœ ˜,K˜%Kšœ3˜3Kšœ'˜'Kšœ'˜'Kšœ)˜)Kšœ˜Kšœ˜Kšœ ˜—Kš œœ œœœ ˜C—šœ˜šœœœ ˜,Kšœ˜Kšœ˜Kšœ ˜—Kšœœ˜!—šœ˜šœœœ ˜,Kšœ˜Kšœ˜Kšœ!˜!Kšœ&˜&Kšœ ˜—Kš œœ œœœ ˜C—˜šœ ˜šœ˜Kšœ œœœ"˜>K˜!Kšœ˜—šœ_˜_Kšœ œœœ"˜>K˜Kšœ˜—šœ˜Kšœ œœœ˜9K˜Kšœ˜—šœ[Ÿ œ˜}šœ œœœ˜#šœœ ˜Kšœ-˜-Kšœ˜—Kšœ˜—K˜Kš œœ œœœœ˜SKšœ˜—šœiŸœ˜~Kšœ˜—Kšœœ˜—K˜—šœ˜Kšœ œœ˜K˜!Kšœ˜—K˜K˜—šœ œ˜Kšœœ˜Kšœ˜Kšœœ˜Kšœ˜ —K˜KšœŸ%˜;Kšœœœ˜šœœ˜'Kšœ)˜)—Jšœ œ˜Jšœœ˜Kšœ[œœ˜nKšœœ˜šœ œ˜Kšœ*Ÿ˜>Kšœœœ˜!Kšœ9˜9K˜!Kšœ˜—K˜!Kšœ&˜&K˜ Kšœ˜Kšœ˜—K˜ Kšœœ˜"K˜——™Kšœ œ˜Kšœœ˜%K˜šžœœ˜š˜Kšœ˜K˜K˜Kšœ˜Kšœ˜—K˜K˜—šžœœœ˜ šœ˜ Kšœ$˜$KšœR˜R—Kšœ˜K˜——™ Kšœœ˜$K˜Kšœœ ˜*KšœœU˜r—K˜—J˜—…—Qπrε