// IFsNameLookup.bcpl -- Network directory lookup routines. // Adapted from PupNetDirLookup.bcpl, but making use of the IFS // name server facilities // Copyright Xerox Corporation 1979, 1980, 1981, 1983 // Last modified September 18, 1983 2:18 PM by Taft get "Pup0.decl" get "Pup1.decl" get "IfsMisc.decl" get "IfsName.decl" external [ // outgoing procedures EnumeratePupAddresses; PrintPort // incoming procedures NameLookup; AddressLookup; PutTemplate OpenLevel1Socket; CloseLevel1Socket; CompletePup GetPBI; ReleasePBI; AppendStringToPup; LocateNet SetTimer; TimerHasExpired; Block; Dismiss; Dequeue; Min Wss; SetBlock; MoveBlock; Zero; DefaultArgs; Allocate; Free // incoming statics sysZone ] manifest maxNPorts = 35 // maximum number of ports we'll consider in answer //---------------------------------------------------------------------------- let EnumeratePupAddresses(name, filter, arg, dontCheckRT; numargs na) = valof //---------------------------------------------------------------------------- // An exact replacement for the Pup package procedure of the same name. // Evaluates name to one or more addresses. For each address, calls // filter(port, arg), where port is that address and arg is the arg passed // to EnumeratePupAddresses. If filter returns true, enumeration stops; if // false, enumeration continues. Ordinarily the addresses are enumerated in // order of increasing distance away from here (hop count), and inaccessible // addresses are omitted; however, if dontCheckRT is supplied and true, // the addresses are not sorted and are all returned. // Filter defaults to TakeTheFirst, which treats arg as a pointer to a Port // and copies the first Pup address to that port, discarding the rest. // Returns zero if successful and an error code (from IfsName.decl) // if unsuccessful. [ DefaultArgs(lv na, -1, TakeTheFirst, 0, false) let ports = Allocate(sysZone, maxNPorts*lenPort) let result = valof [ let nPorts = NameLookup(name, ports, maxNPorts*lenPort) if nPorts ge ecNameFirst then [ // failed, see why unless nPorts eq ecNameServerDisabled resultis nPorts // Reason for failure was that the local name server was locked. // Try asking some other name server before giving up. nPorts = ecNoServerResponded let soc = vec lenPupSoc-1 // Default transient local socket, zero foreign socket OpenLevel1Socket(soc, 0, 0, true) soc>>PupSoc.frnPort.socket↑2 = socketMiscServices for try = 1 to 20 do [ let pbi = GetPBI(soc, true) // try to avoid pbi deadlock if pbi ne 0 then [ AppendStringToPup(pbi, 1, name) pbi>>PBI.allNets = true // send on all directly connected nets CompletePup(pbi, ptNameLookup) ] let wait = nil; SetTimer(lv wait, 100) // 1 sec [ // repeat Block() pbi = Dequeue(lv soc>>PupSoc.iQ) if pbi ne 0 then [ switchon pbi>>PBI.pup.type into [ case ptNetDirError: nPorts = ecNameNotFound endcase case ptNameReply: nPorts = Min((pbi>>PBI.pup.length-pupOvBytes)/6, maxNPorts) MoveBlock(ports, lv pbi>>PBI.pup.words, nPorts*lenPort) endcase ] ReleasePBI(pbi) if nPorts ne ecNoServerResponded break ] ] repeatuntil TimerHasExpired(lv wait) if nPorts ne ecNoServerResponded break ] CloseLevel1Socket(soc) if nPorts ge ecNameFirst resultis nPorts ] // EnumeratePupAddresses (cont'd) // Obtain hop counts for sorting let hopsVec = vec maxNPorts-1 SetBlock(hopsVec, (dontCheckRT? 0, maxHops+1), nPorts) let nRoutesFound = 0 test dontCheckRT ifso // Return all the ports that we got nRoutesFound = nPorts ifnot // Look up route for each port for try = 0 to 20 do [ // Even tries just look in the routing table; odd tries initiate probes. // The purpose of this dance is to get all the information we can from the // existing routing table before initiating probes that might displace // useful entries already there. This can happen if the name we are looking // up maps to more networks than there are entries in the routing cache. let dontProbe = (try & 1) eq 0 for i = 0 to nPorts-1 do if hopsVec!i gr maxHops then [ let rte = LocateNet((ports+i*lenPort)>>Port.net, dontProbe) if rte ne 0 then [ hopsVec!i = rte>>RTE.hops if hopsVec!i le maxHops then nRoutesFound = nRoutesFound+1 ] ] if nRoutesFound eq nPorts % (nRoutesFound gr 0 & try ge 4) break unless dontProbe do Dismiss(100) ] // Emit answers in increasing order of hop count, being careful to maintain // the existing order of addresses at equal distances. // This is effectively an n↑2 sort, but n is small, right? let hops = 0 while hops le maxHops do [ let nextHops = maxHops+1 for i = 0 to nPorts-1 do [ if hopsVec!i eq hops & filter(ports+i*lenPort, arg) then [ nextHops = maxHops+1; break ] if hopsVec!i gr hops then nextHops = Min(nextHops, hopsVec!i) ] hops = nextHops ] resultis nRoutesFound eq 0? ecCantGetThere, 0 ] Free(sysZone, ports) resultis result ] //---------------------------------------------------------------------------- and TakeTheFirst(argPort, resultPort) = valof //---------------------------------------------------------------------------- [ MoveBlock(resultPort, argPort, lenPort) resultis true ] //---------------------------------------------------------------------------- and PrintPort(stream, port) be //---------------------------------------------------------------------------- // Prints a port as a name expression if possible, else as net#host#socket. [ if AddressLookup(stream, port) ne 0 then [ PutTemplate(stream, "$O#$O#", port>>Port.net, port>>Port.host) if port>>Port.socket↑1 ne 0 % port>>Port.socket↑2 ne 0 then PutTemplate(stream, "$UEO", lv port>>Port.socket) ] ]