-- File: NetworkBindingServer.mesa - last edit: -- AOF 19-Jun-87 12:28:34 -- kam 3-Jun-86 10:01:49 -- Copyright (C) 1986, 1987 by Xerox Corporation. All rights reserved. DIRECTORY CommPriorities USING [normal], Courier USING [ Description, DeserializeParameters, EnumerateExports, Error, Exports, FreeEnumeration, Parameters, SerializeParameters, VersionRange], CourierProtocol USING [Protocol3Body, ExchWords], Environment USING [Block, bytesPerWord], Heap USING [systemZone], HostNumbers USING [IsMulticastID], MemoryStream USING [Create, IndexOutOfRange], NetworkBinding USING [ Conjunct, cTRUE, RemoteProgram, dontCare, nullResponse, Predicate, PredicateProcedure, ResponseProc], NetworkBindingInternal, NetworkBindingProtocol, NSBuffer USING [Body, Buffer], NSTypes USING [bytesPerExchangeHeader, ExchangeID, maxDataBytesPerExchange], Process USING [Detach, SetPriority], Router USING [FindMyHostID], Socket USING [ ChannelHandle, Delete, ReturnBuffer, PutPacket, SetPacketBytes, SwapSourceAndDestination], SocketInternal USING [ListenerProcType, CreateListen], SpecialSystem USING [HostNumber], Stream USING [Handle], System USING [ NetworkAddress, broadcastHostNumber, SocketNumber, HostNumber, nullHostNumber]; NetworkBindingServer: MONITOR IMPORTS Courier, CourierProtocol, Heap, HostNumbers, MemoryStream, NetworkBindingInternal, Process, Router, Socket, SocketInternal EXPORTS NetworkBinding, NetworkBindingInternal, System = BEGIN NoBinding: PUBLIC <<NetworkBinding>> ERROR = CODE; DataTooLarge: PUBLIC <<NetworkBinding>> ERROR = CODE; HostNumber: PUBLIC <<System>> TYPE = SpecialSystem.HostNumber; myZone: UNCOUNTED ZONE = Heap.systemZone; bpw: NATURAL = Environment.bytesPerWord; globalExport: RECORD[ count: NATURAL, first, last: Export, sH: Socket.ChannelHandle]; ActiveRecord: TYPE = RECORD[ host: System.HostNumber ← System.nullHostNumber, id: NSTypes.ExchangeID ← [0, 0]]; inProgress: ARRAY NATURAL[0..2) OF ActiveRecord ← ALL[]; Export: TYPE = LONG POINTER TO ExportObject; ExportObject: TYPE = RECORD[ link: Export ← NIL, program: LONG CARDINAL, versionRange: Courier.VersionRange, predicateDescription: Courier.Description, conjunct: NetworkBinding.Conjunct, predicateProc: NetworkBinding.PredicateProcedure, zone: UNCOUNTED ZONE]; me: System.HostNumber = Router.FindMyHostID[]; null: System.HostNumber = System.nullHostNumber; all: System.HostNumber = System.broadcastHostNumber; offsetToCookie: NATURAL = NSTypes.bytesPerExchangeHeader + SIZE[NetworkBindingProtocol.ReturnMessage] * bpw; -- Server Stub Usage ClientProcess: <<DETATCHED PROCESS>> PROC[ b: NSBuffer.Buffer, export: Export, version: CARDINAL, clientInfo: NetworkBindingInternal.ClientInfo] = BEGIN << This task comes in three parts. First we have to deserialize the client's predicate data from the packet we just received (it's pointed to by the procdure argument 'clientInfo'). Then we call the client's predicate proc, passing him a procedure to call if he wants to answer in the affirmative. That procedure will take the client's response and serialize it back into the same buffer the request came in. Then we send the packet back to the sender. The client may elect to raise NoBinding in which case the response data is null (sequence length == 0). We then check to see if it is correct to send back a negative response (was the request broadcasted?). >> ResponseProc: NetworkBinding.ResponseProc = BEGIN block: Environment.Block; block.blockPointer ← LOOPHOLE[@return.body.response[0]]; block.startIndex ← 0; block.stopIndexPlusOne ← NSTypes.maxDataBytesPerExchange - NetworkBindingInternal.fixedBytesInReturn; sH ← MemoryStream.Create[block]; Courier.SerializeParameters[response, sH ! MemoryStream.IndexOutOfRange => GOTO tooBig]; NetworkBindingInternal.SetSequenceLength[ @return.body.response, NATURAL[sH.getPosition[sH]] / bpw]; sH.delete[sH]; --delete the stream EXITS tooBig => {sH.delete[sH]; ERROR DataTooLarge}; END; --ResponseProc sH: Stream.Handle; predicateData: LONG POINTER; predicate: NetworkBinding.Predicate; return: NetworkBindingInternal.ReturnMessage; Process.SetPriority[CommPriorities.normal]; predicateData ← ExtractRequestPredicate[ clientInfo, export.predicateDescription, export.zone ! Courier.Error => GOTO noBinding]; return ← LOOPHOLE[@b.ns.exchangeBody]; predicate ← [clientInfo.remoteProgram, clientInfo.conjunct]; NetworkBindingInternal.SetSequenceLength[@return.body.response, 0]; export.predicateProc[predicate, predicateData, ResponseProc ! NoBinding => GOTO noBinding]; --had nothing to say SendResponse[b, version, return]; --then send the answer, consuming packet EXITS noBinding => SendNoBind[b]; --there's no binding here END; --ClientProcess ExtractRequestPredicate: PROC[ clientInfo: NetworkBindingInternal.ClientInfo, how: Courier.Description, zone: UNCOUNTED ZONE] RETURNS[data: LONG POINTER] = BEGIN Describe: Courier.Description = {notes.noteDisjointData[notes.noteSize[SIZE[LONG POINTER]], how]}; block: Environment.Block; sH: Stream.Handle; block.blockPointer ← LOOPHOLE[@clientInfo.param[0]]; block.startIndex ← 0; block.stopIndexPlusOne ← clientInfo.param.length * bpw; sH ← MemoryStream.Create[block]; Courier.DeserializeParameters[[@data, Describe], sH, zone ! UNWIND => sH.delete[sH]]; sH.delete[sH]; --delete the stream END; --ExtractRequestPredicate DeregisterPredicate: PUBLIC <<NetworkBinding>> ENTRY PROC[ programNumber: LONG CARDINAL, versionRange: Courier.VersionRange, conjunct: NetworkBinding.Conjunct] = BEGIN export, prev: Export ← NIL; FOR export ← globalExport.first, export.link UNTIL export = NIL DO IF export.program = programNumber AND export.versionRange = versionRange AND export.conjunct = conjunct THEN BEGIN IF prev # NIL THEN prev.link ← export.link ELSE globalExport.first ← export.link; IF globalExport.last = export THEN globalExport.last ← prev; IF (globalExport.count ← PRED[globalExport.count]) = 0 THEN Socket.Delete[globalExport.sH]; --away it goes RETURN; END; ENDLOOP; END; --DeregisterPredicate InRange: PROC[low, mid, high: System.HostNumber] RETURNS[BOOLEAN] = BEGIN OPEN l: LOOPHOLE[low, NetworkBindingInternal.ThreeCardinals], m: LOOPHOLE[mid, NetworkBindingInternal.ThreeCardinals], h: LOOPHOLE[high, NetworkBindingInternal.ThreeCardinals]; RETURN[ m.one IN[l.one..h.one] AND m.two IN[l.two..h.two] AND m.three IN[l.three..h.three]]; END; --InRange InProgress: ENTRY PROC[this: ActiveRecord] RETURNS[BOOLEAN] = BEGIN tap: CARDINAL ← LAST[CARDINAL]; FOR i: CARDINAL IN[0..LENGTH[inProgress]) DO IF inProgress[i] = this THEN RETURN[TRUE]; IF inProgress[i].host = System.nullHostNumber THEN tap ← i; ENDLOOP; IF tap # LAST[CARDINAL] THEN inProgress[tap] ← this; RETURN[FALSE]; END; --InProgress ListenerProc: SocketInternal.ListenerProcType = BEGIN body: NSBuffer.Body ← b.ns; hosts: NetworkBindingInternal.Hosts; enum: LONG DESCRIPTOR FOR Courier.Exports; locate: NetworkBindingInternal.CallMessage; return: NetworkBindingInternal.ReturnMessage; clientInfo: NetworkBindingInternal.ClientInfo; return ← LOOPHOLE[(locate ← LOOPHOLE[@body.exchangeBody])]; SELECT TRUE FROM (body.packetType # packetExchange) => GOTO noBinding; --bingo! (body.exchangeType # NetworkBindingProtocol.clientType) => GOTO noBinding; (locate.courierVers # [3, 3]) => GOTO noBinding; (locate.call.type # call) => GOTO noBinding; (locate.call.transaction # 0) => GOTO noBinding; (InProgress[[body.source.host, body.exchangeID]]) => GOTO inProgress; (CourierProtocol.ExchWords[locate.call.program] # NetworkBindingProtocol.program) => GOTO noBinding; (locate.call.version ~IN[ NetworkBindingProtocol.serverVersionLow.. NetworkBindingProtocol.serverVersionHigh]) => GOTO noBinding; (ProcType[locate.call.procedure] # locate) => GOTO noBinding; --it was all a joke, and a bad one at that ENDCASE; hosts ← @locate.body.hosts; clientInfo ← @locate.body.clientInfo + SIZE[NetworkBindingProtocol.HostsRecord[hosts.count]] - SIZE[NetworkBindingProtocol.HostsRecord[0]]; <<Are we in the host list range?>> IF ~InRange[hosts[0], me, hosts[hosts.count - 1]] THEN GOTO noBinding; <<Are we already in the list?>> FOR i: NATURAL IN[0..hosts.count) DO IF hosts[i] = me THEN GOTO noBinding; ENDLOOP; <<Is there a particular program number of interest?>> clientInfo.remoteProgram.programNumber ← CourierProtocol.ExchWords[ clientInfo.remoteProgram.programNumber]; IF clientInfo.remoteProgram # NetworkBinding.dontCare THEN BEGIN <<Does this machine export it?>> enum ← Courier.EnumerateExports[]; FOR i: NATURAL IN[0..LENGTH[enum]) DO OPEN rp: clientInfo.remoteProgram; SELECT TRUE FROM (enum[i].programNumber #rp.programNumber) => LOOP; (enum[i].versionRange.low > rp.version) => LOOP; (enum[i].versionRange.high < rp.version) => LOOP; ENDCASE => EXIT; --life is good - so far REPEAT FINISHED => {Courier.FreeEnumeration[enum]; GOTO noBinding}; ENDLOOP; Courier.FreeEnumeration[enum]; END; <<Is there a predicate to process?>> clientInfo.conjunct ← [CourierProtocol.ExchWords[clientInfo.conjunct]]; IF clientInfo.conjunct # NetworkBinding.cTRUE THEN <<Does this machine know about the predicate?>> FOR export: Export ← globalExport.first, export.link UNTIL export = NIL DO IF export.program = clientInfo.remoteProgram.programNumber AND export.versionRange.low <= clientInfo.remoteProgram.version AND export.versionRange.high >= clientInfo.remoteProgram.version AND export.conjunct = clientInfo.conjunct THEN BEGIN Process.Detach[ FORK ClientProcess[b, export, locate.call.version, clientInfo]]; RETURN; END; REPEAT FINISHED => GOTO noBinding; --we don't know about predicate ENDLOOP; NetworkBindingInternal.SetSequenceLength[@return.body.response, 0]; SendResponse[b, locate.call.version, return]; --fills buffer - consumes 'b' EXITS noBinding => SendNoBind[b]; --consumes 'b' inProgress => Socket.ReturnBuffer[b]; END; --ListenerProc OutProgress: ENTRY PROC[this: ActiveRecord] = BEGIN FOR i: CARDINAL IN[0..LENGTH[inProgress]) DO IF inProgress[i] = this THEN {inProgress[i].host ← System.nullHostNumber; RETURN}; ENDLOOP; END; --OutProgress ProcType: PROCEDURE [procNumber: CARDINAL] -- reverse ORD function RETURNS [NetworkBindingProtocol.Procedure] = MACHINE CODE {}; RegisterPredicate: PUBLIC <<NetworkBinding>> ENTRY PROC [ programNumber: LONG CARDINAL, versionRange: Courier.VersionRange, conjunct: NetworkBinding.Conjunct, proc: NetworkBinding.PredicateProcedure, predicateDescription: Courier.Description, zone: UNCOUNTED ZONE ← NIL] = BEGIN export: Export; IF zone = NIL THEN zone ← myZone; --client didn't specify zone export ← myZone.NEW[ExportObject ← [ program: programNumber, versionRange: versionRange, conjunct: conjunct, predicateProc: proc, zone: zone, predicateDescription: predicateDescription]]; IF globalExport.last # NIL THEN globalExport.last.link ← export ELSE globalExport.first ← export; globalExport.last ← export; IF (globalExport.count ← SUCC[globalExport.count]) = 1 THEN BEGIN globalExport.sH ← SocketInternal.CreateListen[ socket: NetworkBindingProtocol.socket, callback: ListenerProc, clientData: NIL, type: packetExchange]; END; END; --RegisterPredicate SendNoBind: PROC[b: NSBuffer.Buffer] = BEGIN body: NSBuffer.Body ← b.ns; OutProgress[[body.source.host, body.exchangeID]]; --we're finished IF ~HostNumbers.IsMulticastID[@b.ns.destination.host] THEN BEGIN abort: NetworkBindingInternal.AbortMessage; Socket.SwapSourceAndDestination[b]; abort ← LOOPHOLE[@body.exchangeBody]; abort↑ ← [courierVers: [3, 3], abort: [abort[transaction: 0, abort: 1]]]; Socket.SetPacketBytes[b, NSTypes.bytesPerExchangeHeader + (SIZE[NetworkBindingProtocol.AbortMessage] * bpw)]; Socket.PutPacket[globalExport.sH, b]; --send it out (consumes buffer) END ELSE Socket.ReturnBuffer[b]; END; --SendNoBind SendResponse: PROC[ b: NSBuffer.Buffer, version: CARDINAL, return: NetworkBindingInternal.ReturnMessage] = BEGIN body: NSBuffer.Body ← b.ns; bytes: NATURAL = IF version = NetworkBindingProtocol.serverVersionLow THEN 0 ELSE bpw * SIZE[NetworkBindingProtocol.Cookie[return.body.response.length]]; OutProgress[[body.source.host, body.exchangeID]]; --done with this one Socket.SwapSourceAndDestination[b]; --get it going back to him return.courierVers ← [3, 3]; --version of Courier we speak return.return ← [return[transaction: 0]]; --we're answering return.body.responder ← body.source.host; --fixed portion of answer Socket.SetPacketBytes[b, NetworkBindingInternal.fixedBytesInReturn + bytes]; Socket.PutPacket[globalExport.sH, b]; --send it out (consumes buffer) END; --SendResponse StartProtocol: PUBLIC <<NetworkBindingInternal>> PROC[] RETURNS[BOOLEAN] = BEGIN IF globalExport.count = 0 THEN RegisterPredicate[ NetworkBinding.dontCare.programNumber, [NetworkBinding.dontCare.version, NetworkBinding.dontCare.version], NetworkBinding.cTRUE, NIL, NIL, NIL]; --starts vanilla listener RETURN[TRUE]; --can't fail END; --StartProtocol StopProtocol: PUBLIC <<NetworkBindingInternal>> PROC[] RETURNS[BOOLEAN] = BEGIN IF globalExport.count # 1 THEN RETURN[FALSE]; --Too many clients to stop DeregisterPredicate[ NetworkBinding.dontCare.programNumber, [NetworkBinding.dontCare.version, NetworkBinding.dontCare.version], NetworkBinding.cTRUE]; RETURN[TRUE]; END; --StopProtocol --MAINLINE CODE-- globalExport ← [0, NIL, NIL, ]; --get the global data started [] ← StartProtocol[]; --get things rolling END...