-- File: ListenerImpl.mesa - last edit:
-- AOF                 15-Feb-88 19:30:05
-- KAM                  9-Apr-85 10:05:41
-- Copyright (C) 1984, 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  CourierInternal USING [ExchWords],
  CourierProtocol USING [ProtocolRange, RejectCode],
  Driver USING [NetworkNonExistent],
  EthernetDriverFriends, 
  Heap USING [systemZone],
  HostNumbers USING [HostNumber, IsMulticastID],
  Listener USING [InfoProc, PacketType],
  NSBuffer USING [Buffer],
  PhysicalVolume USING [GetAttributes, GetContainingPhysicalVolume],
  Profile USING [GetUser],
  Router USING [FindMyHostID],
  Runtime USING [GetBuildTime, IsBound],
  Socket USING [
    ChannelHandle, Delete, PutPacket, ReturnBuffer, SetPacketWords,
    SwapSourceAndDestination],
  SocketInternal USING [CreateListen, ListenerProcType],
  SpecialSystem USING [timeBooted],
  String USING [StringBoundsFault],
  System USING [HostNumber],
  Time USING [Current],
  Version USING [Append],
  Volume USING [GetLabelString, nullID, systemID],
  WSInfo USING [
    Message, PacketType, pexOverhead, procedure, program, Request,
    Response, sizeLocateReply, sizePilotData, sizePilotInfoReply,
    sizeXdeData, sizeXdeInfoReply, socket, strMax, verMax, version],
  WSInfoExtras;

ListenerImpl: MONITOR
  IMPORTS
    CourierInternal, Driver, EthernetDriverFriends, Heap, HostNumbers,
    PhysicalVolume, Profile, Router, Runtime, Socket, SocketInternal,
    SpecialSystem, String, Time, Version, Volume, WSInfoExtras
  EXPORTS Listener =
  BEGIN

  myAddress: System.HostNumber ← Router.FindMyHostID[];
  myAddr: LONG POINTER ← @myAddress;
  zone: UNCOUNTED ZONE = Heap.systemZone;

  listenerRunning: PUBLIC BOOLEAN ← FALSE;

  ch: Socket.ChannelHandle;

  Range: CourierProtocol.ProtocolRange = [protocol3, protocol3];

  Watcher: SocketInternal.ListenerProcType = {
    h: LONG POINTER TO WSInfo.Message ← LOOPHOLE[@b.ns.exchangeBody];
    WITH messageBody: h↑ SELECT FROM
      call =>
	SELECT TRUE FROM
	  messageBody.program # CourierInternal.ExchWords[WSInfo.program] =>
	    SendErrorReply[b, noSuchProgramNumber, messageBody.transaction];
	  messageBody.procedure # WSInfo.procedure =>
	    SendErrorReply[b, noSuchProcedureValue, messageBody.transaction];
	  messageBody.version # WSInfo.version =>
	    SendErrorReply[b, noSuchVersionNumber, messageBody.transaction];
	  ENDCASE =>
	    WITH body: messageBody.request SELECT FROM
	      locateRequest =>
		IF InRange[@body.first, @body.last] AND NOT InSequence[@body]
		  THEN SendReply[b, locateReply, messageBody.transaction]
		ELSE Socket.ReturnBuffer[b];
	      pilotInfoRequest =>
		SendReply[b, pilotInfoReply, messageBody.transaction];
	      xdeInfoRequest =>
		SendReply[b, xdeInfoReply, messageBody.transaction];
	      ENDCASE =>
		SendReply[b, messageBody.request.type, messageBody.transaction];
      ENDCASE => Socket.ReturnBuffer[b]};

  SendErrorReply: PROCEDURE [
    b: NSBuffer.Buffer, type: CourierProtocol.RejectCode, trans: CARDINAL] = {
    msg: LONG POINTER TO reject WSInfo.Message ← LOOPHOLE[@b.ns.exchangeBody];

    IF HostNumbers.IsMulticastID[LOOPHOLE[@b.ns.destination.host]] THEN {
      Socket.ReturnBuffer[b]; RETURN};

    Socket.SwapSourceAndDestination[b];
    msg↑ ← [protRange: Range, body: reject[transaction:trans, rejectBody:]];
    SELECT type FROM
      noSuchProgramNumber => {
        msg.rejectBody ← noSuchProgramNumber[];
        Socket.SetPacketWords[
          b,
          WSInfo.pexOverhead + SIZE[
            noSuchProgramNumber reject WSInfo.Message]]};
      noSuchProcedureValue => {
        msg.rejectBody ← noSuchProcedureValue[];
        Socket.SetPacketWords[
          b,
          WSInfo.pexOverhead + SIZE[
            noSuchProcedureValue reject WSInfo.Message]]};
      invalidArguments => {
        msg.rejectBody ← invalidArguments[];
        Socket.SetPacketWords[
          b,
          WSInfo.pexOverhead + SIZE[invalidArguments reject WSInfo.Message]]};
      noSuchVersionNumber => {
        msg.rejectBody ← noSuchVersionNumber[[WSInfo.version, WSInfo.version]];
        Socket.SetPacketWords[
          b,
          WSInfo.pexOverhead + SIZE[
            noSuchVersionNumber reject WSInfo.Message]]};
      ENDCASE;
    Socket.PutPacket[ch, b]};

  DataRec: TYPE = MACHINE DEPENDENT RECORD[
    type(0): Listener.PacketType, 
    size(1): CARDINAL,
    data(2): ARRAY [0..0) OF UNSPECIFIED];
  
  SendReply: PROCEDURE [
    b: NSBuffer.Buffer, type: WSInfo.PacketType, trans: CARDINAL] = {
    msg: LONG POINTER TO return WSInfo.Message ← LOOPHOLE[@b.ns.exchangeBody];
    msg↑ ← [Range, return[transaction: trans, response:]];
    Socket.SwapSourceAndDestination[b];
    SELECT type FROM
      pilotInfoReply => {
        Socket.SetPacketWords[b, WSInfo.sizePilotInfoReply];
        msg.response ← [
          pilotInfoReply[
          size: WSInfo.sizePilotData, time:, timeBooted:, 
	  bfCreateDate:, sysVolName:, pVName:]];
        GetPilotInfo[LOOPHOLE[@msg.response]]};
      xdeInfoReply => {
        Socket.SetPacketWords[b, WSInfo.sizeXdeInfoReply];
        msg.response ← [xdeInfoReply[
	  size: WSInfo.sizeXdeData, loggedIn:, bfVersion:, userName:]];
        GetXDEInfo[LOOPHOLE[@msg.response]]};
      locateReply => {
        Socket.SetPacketWords[b, WSInfo.sizeLocateReply];
        msg.response ← [locateReply[size:0]]};
      LOOPHOLE[WSInfoExtras.PacketType[ethernetInfo]] =>
        BEGIN
	OPEN
	  etherReq: LOOPHOLE[msg, WSInfoExtras.EthernetRequest],
	  etherRes: LOOPHOLE[msg, WSInfoExtras.EthernetResponse];
	ENABLE Driver.NetworkNonExistent => GOTO nodevice;
	unit: NATURAL = etherReq.unit;  --copy this out before resusing buffer
	Socket.SetPacketWords[b, SIZE[EthernetDriverFriends.EtherStatsInfo]];
	etherRes ← [ethernetInfo[
	  info: EthernetDriverFriends.GetEthernetStats[unit]]];
	WSInfoExtras.SwapLongsInPlace[@etherRes.info.packetsRecv,
	  SIZE[EthernetDriverFriends.EtherStatsInfo] / SIZE[LONG CARDINAL]];
	EXITS nodevice => SendErrorReply[b, invalidArguments, trans]; 
	END;
      LOOPHOLE[WSInfoExtras.PacketType[ethernetOneInfo]] =>
        BEGIN
	OPEN
	  etherReq: LOOPHOLE[msg, WSInfoExtras.EthernetOneRequest],
	  etherRes: LOOPHOLE[msg, WSInfoExtras.EthernetOneResponse];
	ENABLE Driver.NetworkNonExistent => GOTO nodevice;
	unit: NATURAL = etherReq.unit;  --copy this out before resusing buffer
	Socket.SetPacketWords[b, SIZE[EthernetDriverFriends.EtherStatsInfo]];
	etherRes ← [ethernetOneInfo[
	  info: EthernetDriverFriends.GetEthernetOneStats[unit]]];
	WSInfoExtras.SwapLongsInPlace[@etherRes.info.packetsRecv,
	  SIZE[EthernetDriverFriends.EtherStatsInfo] / SIZE[LONG CARDINAL]];
	EXITS nodevice => SendErrorReply[b, invalidArguments, trans]; 
	END;
      ENDCASE => {
        proc: Listener.InfoProc;
	clientData: LONG POINTER; 
	lp: LONG POINTER TO DataRec ← LOOPHOLE[@msg.response];
	[proc, clientData] ← FindProc[type];
	IF proc = NIL THEN {
	  SendErrorReply[b, invalidArguments, trans];
	  RETURN}
	ELSE {
	  lp.type ← type;
	  lp.size ← proc[type, @lp.data, clientData];
	  Socket.SetPacketWords[b, WSInfo.sizeLocateReply + lp.size];
	  };
	};
    Socket.PutPacket[ch, b]};

  Length: CARDINAL = SIZE[System.HostNumber];
  LOP: TYPE = LONG POINTER TO ARRAY [0..Length) OF CARDINAL;

  InRange: PROCEDURE [first, last: LONG POINTER] RETURNS [yes: BOOLEAN ← TRUE] =
    {
    i: CARDINAL ← 0;
    it: LOP ← first;
    me: LOP ← myAddr;
    FOR i IN [0..Length) DO
      IF it[i] > me[i] THEN RETURN[FALSE] ELSE IF it[i] < me[i] THEN EXIT;
      ENDLOOP;
    it ← last;
    FOR i IN [0..Length) DO
      IF it[i] < me[i] THEN RETURN[FALSE] ELSE IF it[i] > me[i] THEN EXIT;
      ENDLOOP};

  InSequence: PROCEDURE [seq: LONG POINTER TO locateRequest WSInfo.Request]
    RETURNS [yes: BOOLEAN ← FALSE] = {
    me: LOP = myAddr;
    FOR i: CARDINAL IN [0..seq.count) DO
      it: LOP = LOOPHOLE[@seq.hosts[i]];
      FOR j: CARDINAL IN [0..Length) DO
        IF it[j] > me[j] THEN RETURN[FALSE] ELSE IF it[j] < me[j] THEN EXIT;
        REPEAT FINISHED => RETURN[TRUE];
        ENDLOOP;
      ENDLOOP};

  GetPilotInfo: PROCEDURE [p: LONG POINTER TO pilotInfoReply WSInfo.Response] =
    {
    sysVol: LONG STRING ← [WSInfo.strMax];
    pVol: LONG STRING ← [WSInfo.strMax];
    length, i: CARDINAL ← 0;
    IF Volume.systemID # Volume.nullID THEN {
      Volume.GetLabelString[Volume.systemID, sysVol];
      [] ← PhysicalVolume.GetAttributes[
        PhysicalVolume.GetContainingPhysicalVolume[Volume.systemID], pVol]};
    p.sysVolName.length ← WSInfo.strMax;
    length ← MIN[sysVol.length, WSInfo.strMax];
    FOR i IN [0..length) DO p.sysVolName.string[i] ← sysVol[i] ENDLOOP;
    FOR i IN [length..WSInfo.strMax) DO p.sysVolName.string[i] ← 0C ENDLOOP;
    p.pVName.length ← WSInfo.strMax;
    length ← MIN[pVol.length, WSInfo.strMax];
    FOR i IN [0..length) DO p.pVName.string[i] ← pVol[i] ENDLOOP;
    FOR i IN [length..WSInfo.strMax) DO p.pVName.string[i] ← 0C ENDLOOP;
    p.time ← [CourierInternal.ExchWords[Time.Current[]]];
    p.timeBooted ← [CourierInternal.ExchWords[SpecialSystem.timeBooted]];
    p.bfCreateDate ← [CourierInternal.ExchWords[Runtime.GetBuildTime[]]]};

  GetXDEInfo: PROCEDURE [p: LONG POINTER TO xdeInfoReply WSInfo.Response] = {
    version: LONG STRING ← [WSInfo.verMax];
    length, i: CARDINAL ← 0;
    Get: PROCEDURE [name: LONG STRING, password: LONG STRING] = {
      p.userName.length ← WSInfo.strMax;
      length ← IF name = NIL THEN 0 ELSE MIN[name.length, 40];
      FOR i IN [0..length) DO p.userName.string[i] ← name[i] ENDLOOP;
      FOR i IN [length..40) DO p.userName.string[i] ← 0C ENDLOOP;
      p.loggedIn ← NOT (password = NIL OR password.length = 0);
      };
    IF Runtime.IsBound[LOOPHOLE[Profile.GetUser]] THEN Profile.GetUser[Get]
    ELSE Get[NIL, NIL];
    IF Runtime.IsBound[LOOPHOLE[Version.Append]] THEN
      Version.Append[version ! String.StringBoundsFault => {ns ← NIL; RESUME}];
    p.bfVersion.length ← WSInfo.verMax;
    length ← MIN[version.length, WSInfo.verMax];
    FOR i IN [0..length) DO p.bfVersion.string[i] ← version[i] ENDLOOP;
    FOR i IN [length..WSInfo.verMax) DO p.bfVersion.string[i] ← 0C ENDLOOP};

  StartListener: PUBLIC ENTRY PROCEDURE = {
    ENABLE UNWIND => NULL;
    IF listenerRunning THEN RETURN;
    ch ← SocketInternal.CreateListen[WSInfo.socket, Watcher, NIL];
    listenerRunning ← TRUE};

  StopListener: PUBLIC ENTRY PROCEDURE = {
    IF NOT listenerRunning THEN RETURN;
    Socket.Delete[ch]; listenerRunning ← FALSE};

  ProcList: TYPE = LONG POINTER TO ProcRecord;
  ProcRecord: TYPE = RECORD [
    proc: Listener.InfoProc,
    packetType: Listener.PacketType,
    clientData: LONG POINTER,
    link: ProcList];
  procs: ProcList ← NIL;

  Register: PUBLIC ENTRY PROC[
    packetType: Listener.PacketType, proc: Listener.InfoProc,
    clientData: LONG POINTER ← NIL] = {
    ENABLE UNWIND => NULL;
    p: ProcList;
    FOR p ← procs, p.link UNTIL p = NIL DO
      IF p.packetType = packetType THEN {
        p.proc ← proc;
	p.clientData ← clientData;
	RETURN};
      ENDLOOP;
    procs ← zone.NEW[ProcRecord ← [
      proc: proc, packetType: packetType, clientData: clientData, link: procs]]};
    
  Unregister: PUBLIC ENTRY PROC[
    packetType: Listener.PacketType] RETURNS [proc: Listener.InfoProc] = {
    ENABLE UNWIND => NULL;
    p: LONG POINTER TO ProcList;
    FOR p ← @procs, @p.link WHILE p↑ # NIL DO
      IF p.packetType = packetType THEN {
        flush: ProcList ← p↑;
	proc ← flush.proc;
        p↑ ← p.link;
	zone.FREE[@flush];
	RETURN};
      ENDLOOP;
    RETURN[NIL];
    };
    
  FindProc: ENTRY PROC[
    packetType: Listener.PacketType] RETURNS [
      proc: Listener.InfoProc, clientData: LONG POINTER] = {
    ENABLE UNWIND => NULL;
    p: ProcList;
    FOR p ← procs, p.link UNTIL p = NIL DO
      IF p.packetType = packetType THEN {
	proc ← p.proc;
	clientData ← p.clientData;
	RETURN};
      ENDLOOP;
    RETURN[NIL, NIL];
    };
    
  StartListener[];

  END...

LOG
 3-Sep-87 16:47:25  AOF  Put in same fix as I did for BWS (aka StringBoundsFault)  
23-Sep-87 15:01:16  AOF  ENABLE UNWIND => NULL in StartListener