-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- BootServerInfoServer.mesa, HGM, 23-Sep-83  3:38:16

DIRECTORY
  Courier USING [
    Arguments, Description, ExportRemoteProgram, Handle, nullParameters,
    SerializeParameters, NoSuchProcedureNumber, Parameters, Results, UnexportRemoteProgram],
  ExpeditedCourier USING [
    DispatcherProc, ExpeditedServiceHandle, ExportExpeditedPrograms,
    GetDefaultSocketNumber, normalReturnHeader, SerializeHeader, Services, Service,
    UnexportExpeditedPrograms],
  Heap USING [systemZone],
  NetworkStream USING [],
  Stream USING [Handle],
  String USING [Copy],
  System USING [gmtEpoch, NetworkAddress, SocketNumber],

  NSConstants USING [bootServerSocket],
  Socket USING [LocalAddressFromSocket],

  BootServer USING [Counters, GetStatistics],
  BootServerFriends USING [BootFile, EnumerateBootTable],
  BootServerInfo USING [
    BootFileNumber, programNumber, Counters,
    DescribeBootFileNumber, DescribeBootServerInfo,
    DescribeCounters, DescribeNetworkAddress,
    endEnumeration, Info, Procedures, versionRange];

BootServerInfoServer: PROGRAM
  IMPORTS
    Courier, ExpeditedCourier, Heap, String, Socket,
    BootServer, BootServerFriends, BootServerInfo =
  BEGIN

  started: BOOLEAN ← FALSE;

  expeditedServiceHandle: ExpeditedCourier.ExpeditedServiceHandle;

  netMgtName: LONG STRING = "Boot Server";

  z: UNCOUNTED ZONE = Heap.systemZone;
	
    StartServer: PUBLIC PROCEDURE =
      BEGIN
      services: ExpeditedCourier.Services ← DESCRIPTOR[service];
      service: ARRAY [0..2) OF ExpeditedCourier.Service ← [
        [
        versionRange: BootServerInfo.versionRange,
        programNumber: BootServerInfo.programNumber * 200000B,  -- WORD ORDER KROCK
	bindRequestProcedure: BootServerInfo.Procedures.whoAreYou.ORD,
	dispatcher: PktExchngDispatcher],
        [
        versionRange: BootServerInfo.versionRange,
        programNumber: BootServerInfo.programNumber,
	bindRequestProcedure: BootServerInfo.Procedures.whoAreYou.ORD,
	dispatcher: PktExchngDispatcher] ];
      IF started THEN RETURN;
      started ← TRUE;
      Courier.ExportRemoteProgram[
        BootServerInfo.programNumber, BootServerInfo.versionRange,
        Dispatcher, netMgtName, Heap.systemZone, transactional];
      expeditedServiceHandle ← ExpeditedCourier.ExportExpeditedPrograms[
        services: services,
        socket: ExpeditedCourier.GetDefaultSocketNumber[]];
      END;

    StopServer: PUBLIC PROCEDURE =
      BEGIN
      IF ~started THEN RETURN;
      started ← FALSE;
      Courier.UnexportRemoteProgram[BootServerInfo.programNumber, BootServerInfo.versionRange];
      ExpeditedCourier.UnexportExpeditedPrograms[expeditedServiceHandle];
      END;


  -- Dispatchers

    Dispatcher: PROCEDURE [cH: Courier.Handle, procedureNumber: CARDINAL,
    arguments: Courier.Arguments, results: Courier.Results] =
      BEGIN
      proc: BootServerInfo.Procedures;
      IF procedureNumber > BootServerInfo.Procedures.LAST.ORD THEN
        ERROR Courier.NoSuchProcedureNumber;
      proc ← VAL[procedureNumber];
      SELECT proc FROM
        whoAreYou => CourierWhoAreYou[arguments, results];
        getCounters => CourierGetCounters[arguments, results];
        enumerateBootTable => CourierEnumerateBootTable[arguments, results];
        ENDCASE => ERROR Courier.NoSuchProcedureNumber;
      END;

    PktExchngDispatcher: ExpeditedCourier.DispatcherProc =
      BEGIN
      proc: BootServerInfo.Procedures;
      IF procedureNumber > BootServerInfo.Procedures.LAST.ORD THEN
        RETURN[FALSE]; -- ERROR Courier.NoSuchProcedureNumber;
      proc ← VAL[procedureNumber];
      SELECT proc FROM
        whoAreYou => PexWhoAreYou[replyMemoryStream];
        getCounters => PexGetCounters[replyMemoryStream];
        ENDCASE => RETURN[FALSE]; -- ERROR Courier.NoSuchProcedureNumber;
      RETURN[TRUE];
      END;


    PexWhoAreYou: PROCEDURE [replyMemoryStream: Stream.Handle] =
      BEGIN
      me: System.NetworkAddress ← GetAddress[];
      -- no arguments
      -- send results
      ExpeditedCourier.SerializeHeader[replyMemoryStream, ExpeditedCourier.normalReturnHeader];
      Courier.SerializeParameters [[@me, BootServerInfo.DescribeNetworkAddress], replyMemoryStream];
      END;

    PexGetCounters: PROCEDURE [replyMemoryStream: Stream.Handle] =
      BEGIN
      counters: BootServerInfo.Counters ← RealGetCounters[];
      -- no arguments
      -- send results
      ExpeditedCourier.SerializeHeader[replyMemoryStream, ExpeditedCourier.normalReturnHeader];
      Courier.SerializeParameters [[@counters, BootServerInfo.DescribeCounters], replyMemoryStream];
      END;

    CourierWhoAreYou: PROCEDURE [
      arguments: Courier.Arguments, results: Courier.Results] =
      BEGIN
      me: System.NetworkAddress ← GetAddress[];
      args: Courier.Parameters ← Courier.nullParameters;
      arguments[args];
      [] ← results[[@me, BootServerInfo.DescribeNetworkAddress]];
      END;

    GetAddress: PROCEDURE RETURNS [System.NetworkAddress] =
      BEGIN
      RETURN[Socket.LocalAddressFromSocket[NSConstants.bootServerSocket]];
      END;

    CourierGetCounters: PROCEDURE [
      arguments: Courier.Arguments, results: Courier.Results] =
      BEGIN
      counters: BootServerInfo.Counters ← RealGetCounters[];
      args: Courier.Parameters ← Courier.nullParameters;
      arguments[args];
      [] ← results[[@counters, BootServerInfo.DescribeCounters]];
      END;

    CourierEnumerateBootTable: PROCEDURE [
      arguments: Courier.Arguments, results: Courier.Results] =
      BEGIN
      in: BootServerInfo.BootFileNumber;
      out: BootServerInfo.Info;
      fileName: STRING = [100];
      args: Courier.Parameters ← [@in, BootServerInfo.DescribeBootFileNumber];
      arguments[args];
      out ← RealEnumerateBootTable[in, fileName];
      [] ← results[[@out, BootServerInfo.DescribeBootServerInfo]];
      END;


    Init: PROCEDURE =
      BEGIN
      StartServer[];
      END;


  RealGetCounters: PROCEDURE RETURNS [answer: BootServerInfo.Counters] =
    BEGIN
    counters: BootServer.Counters ← BootServer.GetStatistics[];
    answer ← [
      bootFilesRequested: counters.bootFilesRequested,
      bootFilesSent: counters.bootFilesSent,
      bootFilesSentSlowly: counters.bootFilesSentSlowly,
      microcodeBootFilesRequested: counters.microcodeBootFilesRequested,
      microcodeBootFilesSent: counters.microcodeBootFilesSent,
      newBootFilesRetrieved: counters.newBootFilesRetrieved];
    END;

  RealEnumerateBootTable: PROCEDURE [
    old: BootServerInfo.BootFileNumber, fileName: STRING]
    RETURNS [new: BootServerInfo.Info] =
    BEGIN
    GetNext: PROCEDURE [bf: BootServerFriends.BootFile] =
      BEGIN
      IF Less[old, bf.code] AND Less[bf.code, new.bfn] THEN
        BEGIN
        new ← [
          bfn: bf.code,
          fileName: fileName,
          fileType: bf.fileType,
          machineType: bf.machineType,
          create: bf.create,
          bytes: bf.bytes,
          count: bf.count,
          ms: bf.ms];
	IF bf.unknown THEN bf.bytes ← 0;
	String.Copy[fileName, bf.fileName];
	END;
      END;
    new ← [
      bfn: BootServerInfo.endEnumeration,
      fileName: NIL,
      fileType: reserved,
      machineType: reserved,
      create: System.gmtEpoch,
      bytes: 0,
      count: 0,
      ms: 0];
    BootServerFriends.EnumerateBootTable[GetNext];
    END;

  Less: PROCEDURE [a, b: BootServerInfo.BootFileNumber] RETURNS [BOOLEAN] =
    BEGIN
    Size: TYPE = [0..SIZE[BootServerInfo.BootFileNumber]);
    aa: POINTER TO ARRAY Size OF WORD ← LOOPHOLE[@a];
    bb: POINTER TO ARRAY Size OF WORD ← LOOPHOLE[@b];
    FOR i: CARDINAL IN Size DO
      IF aa[i] < bb[i] THEN RETURN[TRUE];
      IF aa[i] > bb[i] THEN RETURN[FALSE];
      ENDLOOP;
    RETURN[FALSE];
    END;

  Init[];
  END.