-- 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...